mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Generated
+16
-4244
File diff suppressed because it is too large
Load Diff
+10
-9
@@ -9,32 +9,33 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
authors = ["ASTATIN3"]
|
authors = ["ASTATIN3"]
|
||||||
include = ["LICENSE-APACHE", "LICENSE-MIT", "**/*.rs", "Cargo.toml"]
|
include = ["LICENSE", "**/*.rs", "Cargo.toml"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
# Binaries
|
# Binaries
|
||||||
"ush-gui",
|
# "ush-gui",
|
||||||
|
|
||||||
# UnShell Binaries
|
# UnShell Binaries
|
||||||
"ush-server",
|
# "ush-server",
|
||||||
"ush-payload",
|
"ush-payload",
|
||||||
|
|
||||||
# Libraries
|
# Libraries
|
||||||
"ush-manager",
|
"ush-obfuscate",
|
||||||
"ush-obfuscate"
|
"base62"
|
||||||
, "core-modules/server2"]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
log = []
|
log = []
|
||||||
log_debug = ["log"]
|
log_debug = ["log", "chrono"]
|
||||||
|
|
||||||
obfuscate = ["ush-obfuscate/obfuscate"]
|
obfuscate_aes = ["ush-obfuscate/obfuscate_aes"]
|
||||||
|
obfuscate_ref = ["ush-obfuscate/obfuscate_ref"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, optional = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "base62"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aes = "0.8.4"
|
||||||
|
cbc = "0.1.2"
|
||||||
|
regex = "1.12.3"
|
||||||
|
sha2 = "0.10.9"
|
||||||
@@ -1,3 +1,58 @@
|
|||||||
|
use crate::{base62::Base62, hash};
|
||||||
|
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
|
||||||
|
use cbc::cipher::block_padding::Pkcs7;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
fn pkcs7_padded_length(input_len: usize) -> usize {
|
||||||
|
let block_size = 16;
|
||||||
|
((input_len / block_size) + 1) * block_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt_aes(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
|
||||||
|
let plaintext = plaintext.as_bytes();
|
||||||
|
|
||||||
|
// Hash the env key to get a 32-byte (256-bit) AES key
|
||||||
|
let key = hash(key_str.as_bytes());
|
||||||
|
|
||||||
|
// Generate a pseudo-random salt byte based on the plaintext
|
||||||
|
// I hope this does not break the encryption.
|
||||||
|
let mut salt = 0;
|
||||||
|
|
||||||
|
for byte in plaintext {
|
||||||
|
salt ^= byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut key_salted = key.clone();
|
||||||
|
|
||||||
|
// Salt the key by XORing the salt byte with all the key bytes.
|
||||||
|
// This ensures that the "hash" generated from the plaintext will
|
||||||
|
// make the encrypted result extremely different.
|
||||||
|
for i in 0..32 {
|
||||||
|
key_salted[i] ^= salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf_len = pkcs7_padded_length(plaintext.len());
|
||||||
|
|
||||||
|
let mut buf = vec![0u8; buf_len];
|
||||||
|
let pt_len = plaintext.len();
|
||||||
|
buf[..pt_len].copy_from_slice(&plaintext);
|
||||||
|
|
||||||
|
let mut ct = cbc::Encryptor::<aes::Aes256>::new(&key_salted.into(), &iv.into())
|
||||||
|
.encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
|
||||||
|
.unwrap()
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
// Add the salt byte to the key byte,
|
||||||
|
ct.insert(0, salt);
|
||||||
|
|
||||||
|
// Encode result in base62
|
||||||
|
Base62::encode_full(&ct, &key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt_aes_lines(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
|
||||||
|
format!("_{}_", encrypt_aes(plaintext, key_str, iv))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn decrypt_aes(input: &str, key_str: &str, iv: [u8; 16]) -> Result<String, String> {
|
pub fn decrypt_aes(input: &str, key_str: &str, iv: [u8; 16]) -> Result<String, String> {
|
||||||
// Hash the env key to get a 32-byte (256-bit) AES key
|
// Hash the env key to get a 32-byte (256-bit) AES key
|
||||||
let mut key = hash(key_str.as_bytes());
|
let mut key = hash(key_str.as_bytes());
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::crypt::{STATIC_BYTE_MAP, hash};
|
use crate::{STATIC_BYTE_MAP, hash};
|
||||||
|
|
||||||
// Randomly mapped Base62 characters
|
// Randomly mapped Base62 characters
|
||||||
pub struct Base62 {
|
pub struct Base62 {
|
||||||
@@ -18,19 +18,20 @@ const ENCODING_RATIO: f64 = 8.0 / 5.954196310386875; // 8.0 / log2(62.0)
|
|||||||
impl Base62 {
|
impl Base62 {
|
||||||
pub fn new(key: &[u8], nonce: usize) -> Self {
|
pub fn new(key: &[u8], nonce: usize) -> Self {
|
||||||
// Hash key again, for the chance that this random function can be used to derive the key
|
// Hash key again, for the chance that this random function can be used to derive the key
|
||||||
|
// My solution to not being good at cryptography lol
|
||||||
let key = hash(key);
|
let key = hash(key);
|
||||||
|
|
||||||
let mut charset: [char; 62] = [0 as char; 62];
|
let mut charset: [char; 62] = [0 as char; 62];
|
||||||
|
|
||||||
// Create a vector of indices from 0 to 61
|
// Create a vector of indices from 0 to 61
|
||||||
let mut current_indicies = (0..62).map(|i| i as usize).collect::<Vec<usize>>();
|
let mut current_indices = (0..62).map(|i| i as usize).collect::<Vec<usize>>();
|
||||||
|
|
||||||
// Loop through each byte in the key until all chars are filled
|
// Loop through each byte in the key until all chars are filled
|
||||||
for i in 0..62 as usize {
|
for i in 0..62 as usize {
|
||||||
let rand = STATIC_BYTE_MAP[(key[i as usize % key.len()] as usize + nonce) % 255];
|
let rand = STATIC_BYTE_MAP[(key[i as usize % key.len()] as usize + nonce) % 255];
|
||||||
|
|
||||||
let index_index = rand as usize % current_indicies.len();
|
let index_index = rand as usize % current_indices.len();
|
||||||
let put_index = current_indicies.remove(index_index);
|
let put_index = current_indices.remove(index_index);
|
||||||
|
|
||||||
charset[put_index] = BASE62_CHARS[i];
|
charset[put_index] = BASE62_CHARS[i];
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
pub mod aes_encrypt;
|
mod aes;
|
||||||
#[allow(dead_code)]
|
mod base62;
|
||||||
pub mod base62;
|
|
||||||
|
|
||||||
pub const ENV_KEY_NAME: &str = "OBFUSCATION_KEY";
|
// Exports
|
||||||
pub const BACKUP_ENV_KEY: &str = "OBFUSCATION_KEY_DO_NOT_USE";
|
pub use aes::{decrypt_aes, decrypt_aes_lines, encrypt_aes, encrypt_aes_lines};
|
||||||
|
pub use base62::Base62;
|
||||||
|
|
||||||
pub const STATIC_IV: [u8; 16] = [
|
pub const STATIC_IV: [u8; 16] = [
|
||||||
0x6d, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x69, 0x76, 0x5f, 0x30, 0x31, 0x32,
|
0x6d, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x69, 0x76, 0x5f, 0x30, 0x31, 0x32,
|
||||||
@@ -32,3 +32,19 @@ pub fn hash(input: &[u8]) -> [u8; 32] {
|
|||||||
hasher.update(input);
|
hasher.update(input);
|
||||||
hasher.finalize().into()
|
hasher.finalize().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode_usize(value: usize) -> Vec<u8> {
|
||||||
|
if value == 0 {
|
||||||
|
return vec![0];
|
||||||
|
}
|
||||||
|
let bytes = value.to_be_bytes();
|
||||||
|
let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count();
|
||||||
|
bytes[leading_zeros..].to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_usize(bytes: &[u8]) -> usize {
|
||||||
|
let mut buf = [0u8; size_of::<usize>()];
|
||||||
|
let offset = buf.len() - bytes.len();
|
||||||
|
buf[offset..].copy_from_slice(bytes);
|
||||||
|
usize::from_be_bytes(buf)
|
||||||
|
}
|
||||||
@@ -5,6 +5,9 @@
|
|||||||
# -Z build-std-features= \
|
# -Z build-std-features= \
|
||||||
# --profile minimize -p unshell-payload -- $@
|
# --profile minimize -p unshell-payload -- $@
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
OBFUSCATION_KEY=kjwerkwerkjbwejehrwhje \
|
||||||
cargo build --profile minimize -p ush-payload $@
|
cargo build --profile minimize -p ush-payload $@
|
||||||
|
|
||||||
export BINARY=./target/minimize/ush-payload
|
export BINARY=./target/minimize/ush-payload
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
[unstable]
|
|
||||||
build-std = ["core", "std"]
|
|
||||||
build-std-features = ["optimize_for_size"]
|
|
||||||
Generated
-873
@@ -1,873 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cipher",
|
|
||||||
"cpufeatures",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android_system_properties"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
|
||||||
dependencies = [
|
|
||||||
"bincode_derive",
|
|
||||||
"serde",
|
|
||||||
"unty",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode_derive"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
|
|
||||||
dependencies = [
|
|
||||||
"virtue",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b"
|
|
||||||
dependencies = [
|
|
||||||
"hybrid-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.19.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cbc"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
|
||||||
dependencies = [
|
|
||||||
"cipher",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.2.49"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
|
||||||
dependencies = [
|
|
||||||
"find-msvc-tools",
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
|
||||||
dependencies = [
|
|
||||||
"iana-time-zone",
|
|
||||||
"js-sys",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cipher"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|
||||||
dependencies = [
|
|
||||||
"crypto-common",
|
|
||||||
"inout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "client"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"unshell-lib",
|
|
||||||
"unshell-manager",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation-sys"
|
|
||||||
version = "0.8.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.2.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-channel"
|
|
||||||
version = "0.5.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-common"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.10.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer",
|
|
||||||
"crypto-common",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "find-msvc-tools"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-array"
|
|
||||||
version = "0.14.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"r-efi",
|
|
||||||
"wasip2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex-literal"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hybrid-array"
|
|
||||||
version = "0.4.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone"
|
|
||||||
version = "0.1.64"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
|
||||||
dependencies = [
|
|
||||||
"android_system_properties",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"iana-time-zone-haiku",
|
|
||||||
"js-sys",
|
|
||||||
"log",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone-haiku"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inout"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
|
||||||
dependencies = [
|
|
||||||
"block-padding 0.3.3",
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.83"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.178"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libloading"
|
|
||||||
version = "0.8.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
|
||||||
dependencies = [
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot"
|
|
||||||
version = "0.12.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot_core"
|
|
||||||
version = "0.9.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"smallvec",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.103"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
|
||||||
dependencies = [
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.5.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.12.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
||||||
dependencies = [
|
|
||||||
"serde_core",
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_core"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_json"
|
|
||||||
version = "1.0.145"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"memchr",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha2"
|
|
||||||
version = "0.10.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_init"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"cfg_aliases 0.2.1",
|
|
||||||
"libc",
|
|
||||||
"parking_lot",
|
|
||||||
"parking_lot_core",
|
|
||||||
"static_init_macro",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_init_macro"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f"
|
|
||||||
dependencies = [
|
|
||||||
"cfg_aliases 0.1.1",
|
|
||||||
"memchr",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.111"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-crypt"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"aes",
|
|
||||||
"block-padding 0.4.2",
|
|
||||||
"cbc",
|
|
||||||
"getrandom",
|
|
||||||
"hex",
|
|
||||||
"hex-literal",
|
|
||||||
"regex",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-lib"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bincode",
|
|
||||||
"chrono",
|
|
||||||
"crossbeam-channel",
|
|
||||||
"libc",
|
|
||||||
"rand",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-manager"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bincode",
|
|
||||||
"libc",
|
|
||||||
"libloading",
|
|
||||||
"rand",
|
|
||||||
"unshell-lib",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-obfuscate"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rand",
|
|
||||||
"static_init",
|
|
||||||
"syn 2.0.111",
|
|
||||||
"unshell-crypt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unty"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "virtue"
|
|
||||||
version = "0.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasip2"
|
|
||||||
version = "1.0.1+wasi-0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"rustversion",
|
|
||||||
"wasm-bindgen-macro",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.62.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement",
|
|
||||||
"windows-interface",
|
|
||||||
"windows-link",
|
|
||||||
"windows-result",
|
|
||||||
"windows-strings",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.60.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.59.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-link"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wit-bindgen"
|
|
||||||
version = "0.46.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
cargo-features = ["trim-paths", "panic-immediate-abort"]
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "client"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
log_debug = ["unshell-lib/log_debug", "unshell-manager/obfuscate"]
|
|
||||||
obfuscate = ["unshell-lib/obfuscate", "unshell-obfuscate/obfuscate", "unshell-manager/obfuscate"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
unshell-lib = {path = "../../unshell-lib", default-featues = false}
|
|
||||||
unshell-obfuscate = {path = "../../unshell-obfuscate", default-featues = false}
|
|
||||||
unshell-manager = {path = "../../unshell-manager", default-featues = false}
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
strip = true # Strip symbols from the binary
|
|
||||||
opt-level = "s" # Optimize for size
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
panic = "immediate-abort"
|
|
||||||
debug = false
|
|
||||||
trim-paths = "all"
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# cargo clean
|
|
||||||
|
|
||||||
OBFUSCATION_KEY=abc123abc \
|
|
||||||
cargo build --release
|
|
||||||
|
|
||||||
export BINARY=./target/release/libclient.so
|
|
||||||
|
|
||||||
|
|
||||||
declare -a headers=(
|
|
||||||
".gnu_debuglink" # - Debug information link
|
|
||||||
".comment" #- Compiler version info
|
|
||||||
".shstrtab" #- Section header string table (only needed by tools like readelf)
|
|
||||||
".note.gnu.bu" ".note.gnu.build-id" # - Build ID note
|
|
||||||
".eh_frame" ".eh_frame_hdr" # Exception handling info (can break C++ exceptions if removed)
|
|
||||||
".gnu.version"
|
|
||||||
#".gnu.version_r"
|
|
||||||
# Symbol versioning (may be needed for some shared libraries)
|
|
||||||
#".gnu.hash" # Hash table for symbol lookup optimization
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: Implement FAKE section header comments and information
|
|
||||||
# Shuffle order of headers??
|
|
||||||
|
|
||||||
for section in "${headers[@]}"
|
|
||||||
do
|
|
||||||
strip --remove-section="$section" $BINARY
|
|
||||||
echo "Removed section header $section"
|
|
||||||
done
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
use std::{
|
|
||||||
net,
|
|
||||||
sync::{
|
|
||||||
Arc, Mutex,
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
},
|
|
||||||
thread::{self, JoinHandle},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell_lib::{ModuleError, Result, config::RuntimeConfig, debug, error, info};
|
|
||||||
// use unshell_modules::{Manager, ModuleRuntime};
|
|
||||||
|
|
||||||
use unshell_manager::{
|
|
||||||
Manager, ModuleRuntime,
|
|
||||||
network::{Stream, TcpStream},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ClientRuntime {
|
|
||||||
config: &'static RuntimeConfig,
|
|
||||||
thread_handle: Option<JoinHandle<()>>,
|
|
||||||
join_signal: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientRuntime {
|
|
||||||
pub fn new(config: &'static RuntimeConfig) -> Result<ClientRuntime> {
|
|
||||||
let join_signal = Arc::new(AtomicBool::new(false));
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
config,
|
|
||||||
thread_handle: None,
|
|
||||||
join_signal,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn send(&mut self, announcement: &Announcement) -> Result<(), ModuleError> {
|
|
||||||
// let bytes = announcement.encode();
|
|
||||||
|
|
||||||
// let mut streams = self.stream.lock().unwrap();
|
|
||||||
|
|
||||||
// for stream in streams.iter_mut() {
|
|
||||||
// stream.write_all(&u32::to_be_bytes(bytes.len() as u32))?;
|
|
||||||
// stream.write_all(&bytes)?;
|
|
||||||
// stream.flush()?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("Announcement {:?} sent", announcement);
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleRuntime for ClientRuntime {
|
|
||||||
fn is_running(&self) -> bool {
|
|
||||||
self.thread_handle.as_ref().is_none_or(|h| h.is_finished())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kill(self: Box<Self>) {
|
|
||||||
if !self.is_running() {
|
|
||||||
self.join_signal.store(true, Ordering::Relaxed);
|
|
||||||
if let Some(handle) = self.thread_handle {
|
|
||||||
let _ = handle.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self, manager: Arc<Mutex<Manager>>) -> Result<()> {
|
|
||||||
let host = match self.config.config.get("host") {
|
|
||||||
Some(host) => host,
|
|
||||||
None => {
|
|
||||||
return Err(ModuleError::Error(
|
|
||||||
"Could not find HOST in Client Runtime".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let retry = match self.config.config.get("retry") {
|
|
||||||
Some(retry) => Duration::from_millis(retry.parse::<u64>().unwrap()),
|
|
||||||
None => {
|
|
||||||
return Err(ModuleError::Error(
|
|
||||||
"Could not find RETRY in Client Runtime".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// let join_clone = self.join_signal.clone();
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
debug!("Connecting to server...");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let stream = match net::TcpStream::connect(host) {
|
|
||||||
Ok(stream) => stream,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to connect to server: {}", e);
|
|
||||||
thread::sleep(retry);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
info!("Connected to {}", host);
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(100));
|
|
||||||
// Duration::from_millis(100);
|
|
||||||
|
|
||||||
let stream = TcpStream::new(stream);
|
|
||||||
let stream_clone = stream.try_clone().unwrap();
|
|
||||||
|
|
||||||
manager.lock().unwrap().add_connection(stream_clone);
|
|
||||||
|
|
||||||
// while !join_clone.load(Ordering::Relaxed) {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
while stream.is_alive() {
|
|
||||||
thread::sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Disconnected from 1234 {}", host);
|
|
||||||
|
|
||||||
thread::sleep(retry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
mod client_runtime;
|
|
||||||
|
|
||||||
pub const MODULE_NAME: &'static str = "client";
|
|
||||||
|
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
use unshell_lib::{
|
|
||||||
ModuleError,
|
|
||||||
// client::client_runtime::ClientRuntime,
|
|
||||||
config::RuntimeConfig,
|
|
||||||
warn, // module_interface
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell_manager::{InterfaceWrapper, ModuleRuntime, NamedComponent, module_interface};
|
|
||||||
|
|
||||||
use crate::client_runtime::ClientRuntime;
|
|
||||||
|
|
||||||
pub use unshell_lib::logger::setup_logger;
|
|
||||||
|
|
||||||
pub extern "C" fn test1() {
|
|
||||||
warn!("Test1 called xxxxxxxxxxx");
|
|
||||||
}
|
|
||||||
pub extern "C" fn test2() {
|
|
||||||
warn!("Test2 called");
|
|
||||||
}
|
|
||||||
pub extern "C" fn test3() {
|
|
||||||
warn!("Test3 called");
|
|
||||||
}
|
|
||||||
|
|
||||||
module_interface! {
|
|
||||||
ClientInterface {
|
|
||||||
fn test1();
|
|
||||||
fn test2();
|
|
||||||
fn test3();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClientInterfaceWrapper;
|
|
||||||
|
|
||||||
impl InterfaceWrapper for ClientInterfaceWrapper {
|
|
||||||
fn get_interface<T: 'static>(&self) -> Option<T>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<ClientInterface>() {
|
|
||||||
let my_struct = ClientInterface::from_raw(test1, test2, test3);
|
|
||||||
|
|
||||||
unsafe { Some(std::mem::transmute_copy(&my_struct)) }
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_interface() -> Option<&'static (dyn InterfaceWrapper + Sync)> {
|
|
||||||
Some(&ClientInterfaceWrapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_runtime(config: &'static RuntimeConfig) -> Result<Box<dyn ModuleRuntime>, ModuleError> {
|
|
||||||
Ok(Box::new(ClientRuntime::new(config)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub fn get_components() -> Vec<NamedComponent> {
|
|
||||||
vec![NamedComponent {
|
|
||||||
name: MODULE_NAME,
|
|
||||||
get_interface: &get_interface,
|
|
||||||
start_runtime: &start_runtime,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
Generated
-861
@@ -1,861 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cipher",
|
|
||||||
"cpufeatures",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android_system_properties"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
|
||||||
dependencies = [
|
|
||||||
"bincode_derive",
|
|
||||||
"serde",
|
|
||||||
"unty",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode_derive"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
|
|
||||||
dependencies = [
|
|
||||||
"virtue",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b"
|
|
||||||
dependencies = [
|
|
||||||
"hybrid-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.19.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cbc"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
|
||||||
dependencies = [
|
|
||||||
"cipher",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.2.50"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
|
|
||||||
dependencies = [
|
|
||||||
"find-msvc-tools",
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
|
||||||
dependencies = [
|
|
||||||
"iana-time-zone",
|
|
||||||
"js-sys",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cipher"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|
||||||
dependencies = [
|
|
||||||
"crypto-common",
|
|
||||||
"inout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation-sys"
|
|
||||||
version = "0.8.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.2.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-channel"
|
|
||||||
version = "0.5.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-common"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.10.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer",
|
|
||||||
"crypto-common",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "find-msvc-tools"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-array"
|
|
||||||
version = "0.14.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"r-efi",
|
|
||||||
"wasip2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex-literal"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hybrid-array"
|
|
||||||
version = "0.4.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone"
|
|
||||||
version = "0.1.64"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
|
||||||
dependencies = [
|
|
||||||
"android_system_properties",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"iana-time-zone-haiku",
|
|
||||||
"js-sys",
|
|
||||||
"log",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone-haiku"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inout"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
|
||||||
dependencies = [
|
|
||||||
"block-padding 0.3.3",
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.83"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.178"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libloading"
|
|
||||||
version = "0.8.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
|
||||||
dependencies = [
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot"
|
|
||||||
version = "0.12.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot_core"
|
|
||||||
version = "0.9.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"smallvec",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.103"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
|
||||||
dependencies = [
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.5.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.12.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
||||||
dependencies = [
|
|
||||||
"serde_core",
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_core"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_json"
|
|
||||||
version = "1.0.145"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"memchr",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "server"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"unshell-lib",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha2"
|
|
||||||
version = "0.10.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_init"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"cfg_aliases 0.2.1",
|
|
||||||
"libc",
|
|
||||||
"parking_lot",
|
|
||||||
"parking_lot_core",
|
|
||||||
"static_init_macro",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_init_macro"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f"
|
|
||||||
dependencies = [
|
|
||||||
"cfg_aliases 0.1.1",
|
|
||||||
"memchr",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.111"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-crypt"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"aes",
|
|
||||||
"block-padding 0.4.2",
|
|
||||||
"cbc",
|
|
||||||
"getrandom",
|
|
||||||
"hex",
|
|
||||||
"hex-literal",
|
|
||||||
"regex",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-lib"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bincode",
|
|
||||||
"chrono",
|
|
||||||
"crossbeam-channel",
|
|
||||||
"libc",
|
|
||||||
"libloading",
|
|
||||||
"rand",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-obfuscate"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rand",
|
|
||||||
"static_init",
|
|
||||||
"syn 2.0.111",
|
|
||||||
"unshell-crypt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unty"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "virtue"
|
|
||||||
version = "0.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasip2"
|
|
||||||
version = "1.0.1+wasi-0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"rustversion",
|
|
||||||
"wasm-bindgen-macro",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.62.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement",
|
|
||||||
"windows-interface",
|
|
||||||
"windows-link",
|
|
||||||
"windows-result",
|
|
||||||
"windows-strings",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.60.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.59.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-link"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wit-bindgen"
|
|
||||||
version = "0.46.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
cargo-features = ["trim-paths", "panic-immediate-abort"]
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "server"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
log_debug=["unshell-lib/log_debug"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
unshell-lib = {path = "../../unshell-lib", default-featues = false}
|
|
||||||
unshell-obfuscate = {path = "../../unshell-obfuscate", default-featues = false}
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
strip = true # Strip symbols from the binary
|
|
||||||
opt-level = "s" # Optimize for size
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
panic = "immediate-abort"
|
|
||||||
debug = false
|
|
||||||
trim-paths = "all"
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
OBFUSCATION_KEY=abc123abc \
|
|
||||||
cargo build --release
|
|
||||||
|
|
||||||
export BINARY=./target/release/libserver.so
|
|
||||||
|
|
||||||
|
|
||||||
declare -a headers=(
|
|
||||||
".gnu_debuglink" # - Debug information link
|
|
||||||
".comment" #- Compiler version info
|
|
||||||
".shstrtab" #- Section header string table (only needed by tools like readelf)
|
|
||||||
".note.gnu.bu" ".note.gnu.build-id" # - Build ID note
|
|
||||||
".eh_frame" ".eh_frame_hdr" # Exception handling info (can break C++ exceptions if removed)
|
|
||||||
".gnu.version" ".gnu.version_r" # Symbol versioning (may be needed for some shared libraries)
|
|
||||||
".gnu.hash" # Hash table for symbol lookup optimization
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Implement FAKE section header comments and information
|
|
||||||
// Shuffle order of headers??
|
|
||||||
|
|
||||||
for section in "${headers[@]}"
|
|
||||||
do
|
|
||||||
strip --remove-section="$section" $BINARY
|
|
||||||
echo "Removed section header $section"
|
|
||||||
done
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
mod server_runtime;
|
|
||||||
|
|
||||||
pub use server_runtime::ListenerRuntime;
|
|
||||||
|
|
||||||
use unshell_lib::{
|
|
||||||
ModuleError, ModuleRuntime,
|
|
||||||
config::{InterfaceWrapper, NamedComponent, RuntimeConfig},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const COMPONENT_NAME: &'static str = "server";
|
|
||||||
|
|
||||||
fn get_interface() -> Option<&'static (dyn InterfaceWrapper + Sync)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_runtime(config: &'static RuntimeConfig) -> Result<Box<dyn ModuleRuntime>, ModuleError> {
|
|
||||||
Ok(Box::new(ListenerRuntime::new(config)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub const fn get_named_component() -> NamedComponent {
|
|
||||||
NamedComponent {
|
|
||||||
name: COMPONENT_NAME,
|
|
||||||
get_interface: &get_interface,
|
|
||||||
start_runtime: &start_runtime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
use std::{
|
|
||||||
net::TcpListener,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
thread::{self, JoinHandle},
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell_lib::{config::RuntimeConfig, module::Manager, *};
|
|
||||||
|
|
||||||
pub struct ListenerRuntime {
|
|
||||||
config: &'static RuntimeConfig,
|
|
||||||
thread_handle: Option<JoinHandle<()>>,
|
|
||||||
// streams: Arc<Mutex<Vec<TcpStream>>>,
|
|
||||||
// manager: Option<Arc<Mutex<Manager>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListenerRuntime {
|
|
||||||
pub fn new(config: &'static RuntimeConfig) -> Result<Self, ModuleError> {
|
|
||||||
Ok(Self {
|
|
||||||
config,
|
|
||||||
thread_handle: None,
|
|
||||||
// streams: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
// manager: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn send(&mut self, announcement: &Announcement) -> Result<(), std::io::Error> {
|
|
||||||
// let bytes = announcement.encode();
|
|
||||||
|
|
||||||
// let mut streams = self.streams.lock().unwrap();
|
|
||||||
|
|
||||||
// for stream in streams.iter_mut() {
|
|
||||||
// stream.write_all(&u32::to_be_bytes(bytes.len() as u32))?;
|
|
||||||
// stream.write_all(&bytes)?;
|
|
||||||
// stream.flush()?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// debug!("Announcement {:?} sent", announcement);
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn recv(&mut self) -> Result<Announcement, ModuleError> {
|
|
||||||
// let stream = &mut self.streams.lock().unwrap()[0];
|
|
||||||
|
|
||||||
// let mut size_buf = [0u8; 4];
|
|
||||||
// stream.read_exact(&mut size_buf).unwrap();
|
|
||||||
// let size = u32::from_be_bytes(size_buf);
|
|
||||||
|
|
||||||
// let mut buf = vec![0u8; size as usize];
|
|
||||||
|
|
||||||
// stream.read_exact(&mut buf).unwrap();
|
|
||||||
|
|
||||||
// if let Some(announcement) = Announcement::decode(&buf) {
|
|
||||||
// Ok(announcement)
|
|
||||||
// } else {
|
|
||||||
// Err(ModuleError::Error("Failed to decode announcement".into()))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleRuntime for ListenerRuntime {
|
|
||||||
fn init(&mut self, manager: Arc<Mutex<Manager>>) -> Result<(), ModuleError> {
|
|
||||||
let host = match self.config.config.get("host") {
|
|
||||||
Some(host) => host,
|
|
||||||
None => {
|
|
||||||
return Err(ModuleError::Error(
|
|
||||||
"Could not find HOST in Server Runtime".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let listener = TcpListener::bind(host).unwrap();
|
|
||||||
// let streams = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
|
|
||||||
// let streams_clone = streams.clone();
|
|
||||||
|
|
||||||
let thread_handle = thread::spawn(move || {
|
|
||||||
// let streams = streams_clone.clone();
|
|
||||||
for stream in listener.incoming() {
|
|
||||||
let stream = stream.unwrap();
|
|
||||||
debug!("New connection from {}", stream.peer_addr().unwrap());
|
|
||||||
|
|
||||||
let stream = network::TcpStream::new(stream);
|
|
||||||
|
|
||||||
manager.lock().unwrap().add_connection(Box::new(stream));
|
|
||||||
|
|
||||||
// streams.lock().unwrap().push(stream);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.thread_handle = Some(thread_handle);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_running(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kill(self: Box<Self>) {
|
|
||||||
// if let Some(thread)
|
|
||||||
// if !self.thread_handle.is_finished() {
|
|
||||||
// // self.join_signal.store(true, Ordering::Relaxed);
|
|
||||||
// let _ = self.thread_handle.join();
|
|
||||||
// }
|
|
||||||
// // drop(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "server2"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
log = ["unshell/log"]
|
|
||||||
log_debug = ["unshell/log_debug"]
|
|
||||||
|
|
||||||
# [lib]
|
|
||||||
# crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
unshell.path = "../../"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
use unshell::info;
|
|
||||||
|
|
||||||
pub fn log_test_fn() {
|
|
||||||
info!("Testttttttt");
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
// use bincode::{Decode, Encode};
|
|
||||||
|
|
||||||
/// Mostly temporary server message type
|
|
||||||
// #[derive(Clone, Debug, Encode, Decode)]
|
|
||||||
pub enum Announcement {
|
|
||||||
TestAnnouncement(String),
|
|
||||||
// GetRuntimes,
|
|
||||||
// GetRuntimesAck(usize),
|
|
||||||
|
|
||||||
// StartRuntime(RuntimeConfig),
|
|
||||||
// StartRuntimeAck(bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
// const BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
|
|
||||||
|
|
||||||
// impl Announcement {
|
|
||||||
// pub fn encode(&self) -> Vec<u8> {
|
|
||||||
// bincode::encode_to_vec(self, BINCODE_CONFIG).unwrap()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn decode(bytes: &[u8]) -> Option<Self> {
|
|
||||||
// if let Ok((decoded, _)) = bincode::decode_from_slice(&bytes[..], BINCODE_CONFIG) {
|
|
||||||
// Some(decoded)
|
|
||||||
// } else {
|
|
||||||
// None
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
use serde_json::{Value, json};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ModuleError, Result,
|
|
||||||
config::{ConfigStructField, InterfaceData, InterfaceStruct, TreeMessage},
|
|
||||||
warn,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type ConfigStructKeys = Vec<ConfigStructField>;
|
|
||||||
pub type ConfigStructValues = Vec<Value>;
|
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
keys: ConfigStructKeys,
|
|
||||||
values: ConfigStructValues,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn new(keys: ConfigStructKeys) -> Self {
|
|
||||||
let values = keys
|
|
||||||
.iter()
|
|
||||||
.map(|key| match key {
|
|
||||||
ConfigStructField::Header(_) => Value::Null,
|
|
||||||
ConfigStructField::Text(_) => Value::Null,
|
|
||||||
ConfigStructField::String { default, .. } => json!(default),
|
|
||||||
ConfigStructField::Integer { default, .. } => json!(default),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self { keys, values }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&mut self, message: TreeMessage) -> Result<TreeMessage> {
|
|
||||||
match message {
|
|
||||||
TreeMessage::State(InterfaceData::ConfigStruct(values)) => {
|
|
||||||
self.values = values;
|
|
||||||
Ok(TreeMessage::Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TreeMessage::RequestStruct => Ok(TreeMessage::Interface(
|
|
||||||
// InterfaceStruct::ConfigStruct(self.keys.clone()),
|
|
||||||
// )),
|
|
||||||
TreeMessage::RequestState => Ok(TreeMessage::State(InterfaceData::ConfigStruct(
|
|
||||||
self.values.clone(),
|
|
||||||
))),
|
|
||||||
TreeMessage::RequestStructAndValue => Ok(TreeMessage::InterfaceAndValue(
|
|
||||||
InterfaceStruct::ConfigStruct(self.keys.clone()),
|
|
||||||
InterfaceData::ConfigStruct(self.values.clone()),
|
|
||||||
)),
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
warn!("Tree got invalid message");
|
|
||||||
Err(ModuleError::Error("Invalid Request".into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use crate::config::ConfigStructField;
|
|
||||||
|
|
||||||
pub type ConfigStructListKeys = Vec<ConfigStructField>;
|
|
||||||
pub type ConfigStructListValues = Vec<Vec<Value>>;
|
|
||||||
|
|
||||||
pub struct ConfigStructList {
|
|
||||||
keys: ConfigStructListKeys,
|
|
||||||
values: ConfigStructListValues,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigStructList {
|
|
||||||
pub fn new(keys: ConfigStructListKeys) -> Self {
|
|
||||||
// let values = keys
|
|
||||||
// .iter()
|
|
||||||
// .map(|key| match key {
|
|
||||||
// ConfigStructField::Header(_) => Value::Null,
|
|
||||||
// ConfigStructField::Text(_) => Value::Null,
|
|
||||||
// ConfigStructField::String { default, .. } => json!(default),
|
|
||||||
// ConfigStructField::Integer { default, .. } => json!(default),
|
|
||||||
// })
|
|
||||||
// .collect();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
keys,
|
|
||||||
values: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl Tree for ConfigStructList {}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
pub mod config_struct;
|
|
||||||
// pub mod config_struct_list;
|
|
||||||
mod tree;
|
|
||||||
|
|
||||||
pub use tree::{InterfaceData, InterfaceStruct, Tree, TreeMessage};
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RuntimeConfig {
|
|
||||||
pub parent_component: String,
|
|
||||||
pub name: String,
|
|
||||||
pub config: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub enum ConfigStructField {
|
|
||||||
Header(String),
|
|
||||||
Text(String),
|
|
||||||
String {
|
|
||||||
// Default value of string edit in struct
|
|
||||||
#[serde(default)]
|
|
||||||
default: String,
|
|
||||||
max_length: Option<usize>,
|
|
||||||
// Display string edit as password
|
|
||||||
#[serde(default)]
|
|
||||||
protected: bool,
|
|
||||||
},
|
|
||||||
Integer {
|
|
||||||
// Default value of integer in struct
|
|
||||||
#[serde(default)]
|
|
||||||
default: i32,
|
|
||||||
min: Option<i32>,
|
|
||||||
max: Option<i32>,
|
|
||||||
},
|
|
||||||
// Checkbox
|
|
||||||
// Dropdown
|
|
||||||
// Collapsing header
|
|
||||||
// Slider
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{ModuleError, Result, config::config_struct};
|
|
||||||
|
|
||||||
pub trait Tree {
|
|
||||||
fn is_folder() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_children_string(&self) -> Vec<String> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select_child(&mut self, child: &str, _message: TreeMessage) -> Result<TreeMessage>;
|
|
||||||
|
|
||||||
fn get_value(&self, _message: TreeMessage) -> TreeMessage {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_path(&mut self, elements: &mut Vec<&str>, message: TreeMessage) -> Result<TreeMessage> {
|
|
||||||
if elements.is_empty() {
|
|
||||||
return if Self::is_folder() {
|
|
||||||
Ok(TreeMessage::Folder(self.get_children_string()))
|
|
||||||
} else {
|
|
||||||
Ok(self.get_value(message))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = elements.remove(0);
|
|
||||||
|
|
||||||
if Self::is_folder() {
|
|
||||||
self.select_child(child, message)
|
|
||||||
} else {
|
|
||||||
Err(ModuleError::TreeMessageError(
|
|
||||||
"This is a folder, not a file".into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self, path: &str, message: TreeMessage) -> Result<TreeMessage> {
|
|
||||||
let mut path = if path.is_empty() {
|
|
||||||
Vec::new()
|
|
||||||
} else {
|
|
||||||
path.split("/").collect::<Vec<&str>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.get_path(&mut path, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum TreeMessage {
|
|
||||||
RequestState,
|
|
||||||
RequestStruct,
|
|
||||||
RequestStructAndValue,
|
|
||||||
|
|
||||||
State(InterfaceData),
|
|
||||||
// Interface(InterfaceStruct),
|
|
||||||
InterfaceAndValue(InterfaceStruct, InterfaceData),
|
|
||||||
|
|
||||||
Success,
|
|
||||||
Failure,
|
|
||||||
|
|
||||||
Folder(Vec<String>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum InterfaceStruct {
|
|
||||||
ConfigStruct(config_struct::ConfigStructKeys),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum InterfaceData {
|
|
||||||
ConfigStruct(config_struct::ConfigStructValues),
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
pub mod config;
|
|
||||||
mod error;
|
mod error;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
|
|
||||||
mod announcement;
|
|
||||||
|
|
||||||
pub use error::{ModuleError, Result};
|
pub use error::{ModuleError, Result};
|
||||||
|
|
||||||
pub use announcement::Announcement;
|
|
||||||
|
|
||||||
// Re-exports
|
// Re-exports
|
||||||
pub use serde_json::{Value, json};
|
pub use serde_json::{Value, json};
|
||||||
pub use ush_obfuscate as obfuscate;
|
pub use ush_obfuscate as obfuscate;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Macros that are used that just drop the inside variables
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log {
|
||||||
|
($level:expr, $fmt:tt) => {{}};
|
||||||
|
($level:expr, $fmt:tt, $($arg:expr),*) => {{}};
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! log {
|
||||||
|
($level:expr, $fmt:tt) => {{
|
||||||
|
use $crate::obfuscate;
|
||||||
|
let log_result = obfuscate::sym_format!($fmt);
|
||||||
|
|
||||||
|
$crate::logger::add_record(
|
||||||
|
$level,
|
||||||
|
|
||||||
|
#[cfg(feature = "log_debug")]
|
||||||
|
Some(String::from(obfuscate::file_symbol!())),
|
||||||
|
#[cfg(not(feature = "log_debug"))]
|
||||||
|
None,
|
||||||
|
|
||||||
|
#[cfg(feature = "log_debug")]
|
||||||
|
Some(std::time::SystemTime::now()),
|
||||||
|
#[cfg(not(feature = "log_debug"))]
|
||||||
|
None,
|
||||||
|
|
||||||
|
|
||||||
|
log_result
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
($level:expr, $fmt:tt, $($arg:expr),*) => {{
|
||||||
|
use $crate::obfuscate;
|
||||||
|
let log_result = obfuscate::sym_format!($fmt, $($arg),*);
|
||||||
|
|
||||||
|
$crate::logger::add_record(
|
||||||
|
$level,
|
||||||
|
|
||||||
|
#[cfg(feature = "log_debug")]
|
||||||
|
Some(String::from(obfuscate::file_symbol!())),
|
||||||
|
#[cfg(not(feature = "log_debug"))]
|
||||||
|
None,
|
||||||
|
|
||||||
|
#[cfg(feature = "log_debug")]
|
||||||
|
Some(std::time::SystemTime::now()),
|
||||||
|
#[cfg(not(feature = "log_debug"))]
|
||||||
|
None,
|
||||||
|
|
||||||
|
log_result
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#[macro_export]
|
|
||||||
macro_rules! log {
|
|
||||||
($level:expr, $fmt:tt) => {{
|
|
||||||
use $crate::obfuscate;
|
|
||||||
let log_result = obfuscate::format_obs!($fmt);
|
|
||||||
|
|
||||||
$crate::logger::add_record(
|
|
||||||
$level,
|
|
||||||
|
|
||||||
#[cfg(feature = "log_debug")]
|
|
||||||
Some(String::from(obfuscate::file_symbol!())),
|
|
||||||
#[cfg(not(feature = "log_debug"))]
|
|
||||||
None,
|
|
||||||
|
|
||||||
std::time::SystemTime::now(),
|
|
||||||
log_result
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
($level:expr, $fmt:tt, $($arg:expr),*) => {{
|
|
||||||
use $crate::obfuscate;
|
|
||||||
let log_result = obfuscate::format_obs!($fmt, $($arg),*);
|
|
||||||
|
|
||||||
$crate::logger::add_record(
|
|
||||||
$level,
|
|
||||||
|
|
||||||
#[cfg(feature = "log_debug")]
|
|
||||||
Some(String::from(obfuscate::file_symbol!())),
|
|
||||||
#[cfg(not(feature = "log_debug"))]
|
|
||||||
None,
|
|
||||||
|
|
||||||
std::time::SystemTime::now(),
|
|
||||||
log_result
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! debug {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::log!($crate::logger::LogLevel::Debug, $($arg)*)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! info {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::log!($crate::logger::LogLevel::Info, $($arg)*)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warn {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::log!($crate::logger::LogLevel::Warn, $($arg)*)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! error {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::log!($crate::logger::LogLevel::Error, $($arg)*)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
// Macros that are used that just drop the inside variables
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! debug {
|
|
||||||
($fmt:tt) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
}};
|
|
||||||
($fmt:tt, $($arg:expr),*) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
$(let _ = $arg;)*
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! info {
|
|
||||||
($fmt:tt) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
}};
|
|
||||||
($fmt:tt, $($arg:expr),*) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
$(let _ = $arg;)*
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warn {
|
|
||||||
($fmt:tt) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
}};
|
|
||||||
($fmt:tt, $($arg:expr),*) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
$(let _ = $arg;)*
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! error {
|
|
||||||
($fmt:tt) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
}};
|
|
||||||
($fmt:tt, $($arg:expr),*) => {{
|
|
||||||
let _ = $fmt;
|
|
||||||
$(let _ = $arg;)*
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
+36
-4
@@ -1,9 +1,9 @@
|
|||||||
// Choose if the macros are enabled based on the feature setting
|
// Choose if the macros are enabled based on the feature setting
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
pub mod macros;
|
mod log_enabled;
|
||||||
|
|
||||||
#[cfg(not(feature = "log"))]
|
#[cfg(not(feature = "log"))]
|
||||||
pub mod macros_disabled;
|
mod log_disabled;
|
||||||
|
|
||||||
mod pretty_logger;
|
mod pretty_logger;
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ use std::time::SystemTime;
|
|||||||
pub use pretty_logger::PrettyLogger;
|
pub use pretty_logger::PrettyLogger;
|
||||||
pub use pretty_logger::log;
|
pub use pretty_logger::log;
|
||||||
|
|
||||||
|
pub static mut IS_DEFAULT_LOGGER: bool = true;
|
||||||
static mut LOGGER: &dyn Logger = &DefaultLogger;
|
static mut LOGGER: &dyn Logger = &DefaultLogger;
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -27,7 +28,7 @@ pub struct Record {
|
|||||||
log_level: LogLevel,
|
log_level: LogLevel,
|
||||||
location: Option<String>,
|
location: Option<String>,
|
||||||
// line: u32,
|
// line: u32,
|
||||||
time: SystemTime,
|
time: Option<SystemTime>,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,19 +47,21 @@ pub fn set_logger_box(logger: Box<dyn Logger>) {
|
|||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
unsafe {
|
unsafe {
|
||||||
LOGGER = Box::leak(logger);
|
LOGGER = Box::leak(logger);
|
||||||
|
IS_DEFAULT_LOGGER = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_logger(logger: &'static dyn Logger) {
|
pub fn set_logger(logger: &'static dyn Logger) {
|
||||||
unsafe {
|
unsafe {
|
||||||
LOGGER = logger;
|
LOGGER = logger;
|
||||||
|
IS_DEFAULT_LOGGER = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_record(
|
pub fn add_record(
|
||||||
log_level: LogLevel,
|
log_level: LogLevel,
|
||||||
location: Option<String>,
|
location: Option<String>,
|
||||||
time: SystemTime,
|
time: Option<SystemTime>,
|
||||||
message: String,
|
message: String,
|
||||||
) {
|
) {
|
||||||
logger().log(Record {
|
logger().log(Record {
|
||||||
@@ -81,3 +84,32 @@ pub type SetupLogger = extern "C" fn(logger: &'static dyn Logger);
|
|||||||
pub extern "C" fn setup_logger(logger: &'static dyn Logger) {
|
pub extern "C" fn setup_logger(logger: &'static dyn Logger) {
|
||||||
set_logger(logger);
|
set_logger(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Macro Definitions
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! debug {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log!($crate::logger::LogLevel::Debug, $($arg)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! info {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log!($crate::logger::LogLevel::Info, $($arg)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! warn {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log!($crate::logger::LogLevel::Warn, $($arg)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log!($crate::logger::LogLevel::Error, $($arg)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+37
-28
@@ -1,22 +1,9 @@
|
|||||||
use chrono::{DateTime, Utc};
|
|
||||||
|
|
||||||
use crate::logger::{LogLevel, Logger, Record};
|
use crate::logger::{LogLevel, Logger, Record};
|
||||||
|
|
||||||
pub struct PrettyLogger {
|
pub struct PrettyLogger {
|
||||||
output: Option<Box<dyn Fn(&Record)>>,
|
output: Option<Box<dyn Fn(&Record)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// static TRACE_COLOR: &str = "\x1b[34m";
|
|
||||||
static DEBUG_COLOR: &str = "\x1b[36m";
|
|
||||||
static INFO_COLOR: &str = "\x1b[32m";
|
|
||||||
static WARN_COLOR: &str = "\x1b[33m";
|
|
||||||
static ERROR_COLOR: &str = "\x1b[31m";
|
|
||||||
|
|
||||||
static WHITE: &str = "\x1b[97m";
|
|
||||||
static OFF_WHITE: &str = "\x1b[37m";
|
|
||||||
static TIME_COLOR: &str = "\x1b[36m";
|
|
||||||
static GREY: &str = "\x1b[90m";
|
|
||||||
|
|
||||||
impl Logger for PrettyLogger {
|
impl Logger for PrettyLogger {
|
||||||
fn log(&self, message: Record) {
|
fn log(&self, message: Record) {
|
||||||
if let Some(ref func) = self.output {
|
if let Some(ref func) = self.output {
|
||||||
@@ -28,6 +15,11 @@ impl Logger for PrettyLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn log(message: &Record) {
|
pub fn log(message: &Record) {
|
||||||
|
static DEBUG_COLOR: &str = "\x1b[36m";
|
||||||
|
static INFO_COLOR: &str = "\x1b[32m";
|
||||||
|
static WARN_COLOR: &str = "\x1b[33m";
|
||||||
|
static ERROR_COLOR: &str = "\x1b[31m";
|
||||||
|
|
||||||
let log_level = match message.log_level {
|
let log_level = match message.log_level {
|
||||||
LogLevel::Debug => format!("{DEBUG_COLOR}DBUG"),
|
LogLevel::Debug => format!("{DEBUG_COLOR}DBUG"),
|
||||||
LogLevel::Info => format!("{INFO_COLOR}INFO"),
|
LogLevel::Info => format!("{INFO_COLOR}INFO"),
|
||||||
@@ -35,32 +27,49 @@ pub fn log(message: &Record) {
|
|||||||
LogLevel::Error => format!("{ERROR_COLOR}ERR!"),
|
LogLevel::Error => format!("{ERROR_COLOR}ERR!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let date: DateTime<Utc> = message.time.into();
|
match (message.time, &message.location) {
|
||||||
let date = date.to_rfc2822().to_string();
|
(None, None) => {
|
||||||
|
static WHITE: &str = "\x1b[97m";
|
||||||
|
|
||||||
let location = if let Some(ref location) = message.location {
|
println!("{} {WHITE}{}", log_level, message.message,);
|
||||||
location
|
}
|
||||||
} else {
|
|
||||||
&String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
println!(
|
#[cfg(feature = "log_debug")]
|
||||||
"{OFF_WHITE}[{TIME_COLOR}{}{OFF_WHITE}] {} {WHITE}{} {GREY}{}{WHITE}",
|
(Some(time), Some(location)) => {
|
||||||
date, log_level, message.message, location
|
use chrono::{DateTime, Utc};
|
||||||
);
|
|
||||||
|
let date: DateTime<Utc> = time.into();
|
||||||
|
|
||||||
|
static WHITE: &str = "\x1b[97m";
|
||||||
|
static OFF_WHITE: &str = "\x1b[37m";
|
||||||
|
static TIME_COLOR: &str = "\x1b[36m";
|
||||||
|
static GREY: &str = "\x1b[90m";
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"{OFF_WHITE}[{TIME_COLOR}{}{OFF_WHITE}] {} {WHITE}{} {GREY}{}{WHITE}",
|
||||||
|
date, log_level, message.message, location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unreachable!("Invalid log configuration"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrettyLogger {
|
impl PrettyLogger {
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
crate::logger::set_logger_box(Box::new(PrettyLogger { output: None }));
|
if unsafe { crate::logger::IS_DEFAULT_LOGGER } {
|
||||||
|
crate::logger::set_logger_box(Box::new(PrettyLogger { output: None }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_output<T>(output: T)
|
pub fn init_output<T>(output: T)
|
||||||
where
|
where
|
||||||
T: Fn(&Record) + 'static,
|
T: Fn(&Record) + 'static,
|
||||||
{
|
{
|
||||||
crate::logger::set_logger_box(Box::new(PrettyLogger {
|
if !unsafe { crate::logger::IS_DEFAULT_LOGGER } {
|
||||||
output: Some(Box::new(output)),
|
crate::logger::set_logger_box(Box::new(PrettyLogger {
|
||||||
}));
|
output: Some(Box::new(output)),
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -1,15 +1,15 @@
|
|||||||
use crate::obfuscate::symbol;
|
use crate::obfuscate::sym;
|
||||||
|
|
||||||
pub const LOGGER: &'static str = symbol!("Logger");
|
pub const LOGGER: &'static str = sym!("Logger");
|
||||||
|
|
||||||
pub const TYPE_TREE: &'static str = symbol!("Tree");
|
pub const TYPE_TREE: &'static str = sym!("Tree");
|
||||||
pub const TYPE_QUEUE: &'static str = symbol!("Queue");
|
pub const TYPE_QUEUE: &'static str = sym!("Queue");
|
||||||
|
|
||||||
pub const CMD_GET: &'static str = symbol!("Get");
|
pub const CMD_GET: &'static str = sym!("Get");
|
||||||
pub const CMD_POLL: &'static str = symbol!("Poll");
|
pub const CMD_POLL: &'static str = sym!("Poll");
|
||||||
pub const CMD_GET_LENGTH: &'static str = symbol!("GetLength");
|
pub const CMD_GET_LENGTH: &'static str = sym!("GetLength");
|
||||||
pub const CMD_GET_CHILDREN: &'static str = symbol!("GetChildren");
|
pub const CMD_GET_CHILDREN: &'static str = sym!("GetChildren");
|
||||||
|
|
||||||
pub const ERR_UNSUPPORTED_METHOD: &'static str = symbol!("UnsupportedMethod");
|
pub const ERR_UNSUPPORTED_METHOD: &'static str = sym!("UnsupportedMethod");
|
||||||
pub const ERR_INVALID_COMMAND: &'static str = symbol!("InvalidCommand");
|
pub const ERR_INVALID_COMMAND: &'static str = sym!("InvalidCommand");
|
||||||
pub const ERR_INVALID_CHILD: &'static str = symbol!("InvalidChild");
|
pub const ERR_INVALID_CHILD: &'static str = sym!("InvalidChild");
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ush-gui"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
include.workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
targets = [
|
|
||||||
# "x86_64-unknown-linux-gnu",
|
|
||||||
"wasm32-unknown-unknown"
|
|
||||||
]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
unshell = { path="../" }
|
|
||||||
|
|
||||||
# log = "0.4.27"
|
|
||||||
chrono = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
|
|
||||||
reqwest = {version = "0.12.26", features=["json"]}
|
|
||||||
log = "0.4.29"
|
|
||||||
|
|
||||||
# Stuff for app functionality
|
|
||||||
egui = "0.33.0"
|
|
||||||
eframe = { version = "0.33.0", default-features = false, features = [
|
|
||||||
# "accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies.
|
|
||||||
"default_fonts", # Embed the default egui fonts.
|
|
||||||
"glow", # Use the glow rendering backend. Alternative: "wgpu".
|
|
||||||
"persistence", # Enable restoring app state when restarting the app.
|
|
||||||
"wayland", # To support Linux (and CI)
|
|
||||||
"x11", # To support older Linux distributions (restores one of the default features)
|
|
||||||
] }
|
|
||||||
egui_extras = "0.33.2"
|
|
||||||
egui_tiles = "0.14.0"
|
|
||||||
egui-async = "0.2.6"
|
|
||||||
|
|
||||||
|
|
||||||
# Web Stuff
|
|
||||||
wasm-bindgen-futures = "0.4.50"
|
|
||||||
wasm-bindgen = "0.2.108"
|
|
||||||
web-sys = { version = "0.3.85", features = ["Headers", "Request", "RequestInit", "RequestMode", "Response", "Window"] }
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
[build]
|
|
||||||
filehash = false
|
|
||||||
|
|
||||||
# # Proxy /api/* to backend
|
|
||||||
# [[proxy]]
|
|
||||||
# backend = "http://localhost:8081"
|
|
||||||
# rewrite = "/api/"
|
|
||||||
|
|
||||||
# # Proxy /auth/* to backend
|
|
||||||
# [[proxy]]
|
|
||||||
# backend = "http://localhost:8081"
|
|
||||||
# rewrite = "/auth/"
|
|
||||||
|
|
||||||
[[proxy]]
|
|
||||||
backend = "http://localhost:3000/api" # Address to proxy requests to
|
|
||||||
# ws = false # Use WebSocket for this proxy
|
|
||||||
# insecure = true # Disable certificate validation
|
|
||||||
# no_system_proxy = false # Disable system proxy
|
|
||||||
# rewrite = "" # Strip the given prefix off paths
|
|
||||||
# no_redirect = false # Disable following redirects of proxy responses
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "egui Template PWA",
|
|
||||||
"short_name": "egui-template-pwa",
|
|
||||||
"icons": [],
|
|
||||||
"lang": "en-US",
|
|
||||||
"id": "/index.html",
|
|
||||||
"start_url": "./index.html",
|
|
||||||
"display": "standalone",
|
|
||||||
"background_color": "white",
|
|
||||||
"theme_color": "white"
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
var cacheName = "egui-template-pwa";
|
|
||||||
var filesToCache = [
|
|
||||||
"./",
|
|
||||||
"./index.html",
|
|
||||||
"./eframe_template.js",
|
|
||||||
"./eframe_template_bg.wasm",
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Start the service worker and cache all of the app's content */
|
|
||||||
self.addEventListener("install", function (e) {
|
|
||||||
e.waitUntil(
|
|
||||||
caches.open(cacheName).then(function (cache) {
|
|
||||||
return cache.addAll(filesToCache);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Serve cached content when offline */
|
|
||||||
self.addEventListener("fetch", function (e) {
|
|
||||||
e.respondWith(
|
|
||||||
caches.match(e.request).then(function (response) {
|
|
||||||
return response || fetch(e.request);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// export function httpGet(theUrl) {
|
|
||||||
// var xmlHttp = new XMLHttpRequest();
|
|
||||||
// xmlHttp.open("GET", theUrl, false); // false for synchronous request
|
|
||||||
// xmlHttp.send(null);
|
|
||||||
// return xmlHttp.responseText;
|
|
||||||
// }
|
|
||||||
|
|
||||||
function startHttpRequest(callback) {
|
|
||||||
var xmlHttp = new XMLHttpRequest();
|
|
||||||
xmlHttp.onreadystatechange = function () {
|
|
||||||
if (xmlHttp.readyState !== 4) return;
|
|
||||||
|
|
||||||
if (xmlHttp.status == 200) callback(true, xmlHttp.responseText);
|
|
||||||
else if (xmlHttp.status == 401) callback(false, "Unauthorized");
|
|
||||||
else if (xmlHttp.status == 500) callback(false, "Internal Server Error");
|
|
||||||
else callback(false, xmlHttp.responseText);
|
|
||||||
};
|
|
||||||
return xmlHttp;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function httpGet(theUrl, callback) {
|
|
||||||
var xmlHttp = startHttpRequest(callback);
|
|
||||||
xmlHttp.open("GET", theUrl, true); // true for asynchronous
|
|
||||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
|
||||||
xmlHttp.send(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function httpPost(url, body, callback) {
|
|
||||||
var xmlHttp = startHttpRequest(callback);
|
|
||||||
xmlHttp.open("POST", url, true);
|
|
||||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
|
||||||
// var data = JSON.stringify({ email: "[email protected]", password: "101010" });
|
|
||||||
xmlHttp.send(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function httpGetAuth(theUrl, auth, callback) {
|
|
||||||
var xmlHttp = startHttpRequest(callback);
|
|
||||||
xmlHttp.open("GET", theUrl, true); // true for asynchronous
|
|
||||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
|
||||||
xmlHttp.setRequestHeader("authorization", auth);
|
|
||||||
xmlHttp.send(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function httpPostAuth(url, auth, body, callback) {
|
|
||||||
var xmlHttp = startHttpRequest(callback);
|
|
||||||
xmlHttp.open("POST", url, true);
|
|
||||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
|
||||||
xmlHttp.setRequestHeader("authorization", auth);
|
|
||||||
// var data = JSON.stringify({ email: "[email protected]", password: "101010" });
|
|
||||||
xmlHttp.send(body);
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
|
|
||||||
<!-- Disable zooming: -->
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1.0, user-scalable=no"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<!-- change this to your project name -->
|
|
||||||
<title>eframe template</title>
|
|
||||||
|
|
||||||
<!-- config for our rust wasm binary. go to https://trunkrs.dev/assets/#rust for more customization -->
|
|
||||||
<link data-trunk rel="rust" data-wasm-opt="2" />
|
|
||||||
<!-- this is the base url relative to which other urls will be constructed. trunk will insert this from the public-url option -->
|
|
||||||
<base data-trunk-public-url />
|
|
||||||
|
|
||||||
<link data-trunk rel="icon" href="assets/favicon.ico" />
|
|
||||||
|
|
||||||
<link data-trunk rel="copy-file" href="assets/sw.js" />
|
|
||||||
<link data-trunk rel="copy-file" href="assets/manifest.json" />
|
|
||||||
<!--<link
|
|
||||||
data-trunk
|
|
||||||
rel="copy-file"
|
|
||||||
href="assets/icon-1024.png"
|
|
||||||
data-target-path="assets"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
data-trunk
|
|
||||||
rel="copy-file"
|
|
||||||
href="assets/icon-256.png"
|
|
||||||
data-target-path="assets"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
data-trunk
|
|
||||||
rel="copy-file"
|
|
||||||
href="assets/icon_ios_touch_192.png"
|
|
||||||
data-target-path="assets"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
data-trunk
|
|
||||||
rel="copy-file"
|
|
||||||
href="assets/maskable_icon_x512.png"
|
|
||||||
data-target-path="assets"
|
|
||||||
/>-->
|
|
||||||
|
|
||||||
<link rel="manifest" href="manifest.json" />
|
|
||||||
<link rel="apple-touch-icon" href="assets/icon_ios_touch_192.png" />
|
|
||||||
<meta
|
|
||||||
name="theme-color"
|
|
||||||
media="(prefers-color-scheme: light)"
|
|
||||||
content="white"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
name="theme-color"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
content="#404040"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
/* Remove touch delay: */
|
|
||||||
touch-action: manipulation;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
/* Light mode background color for what is not covered by the egui canvas,
|
|
||||||
or where the egui canvas is translucent. */
|
|
||||||
background: #909090;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body {
|
|
||||||
/* Dark mode background color for what is not covered by the egui canvas,
|
|
||||||
or where the egui canvas is translucent. */
|
|
||||||
background: #404040;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow canvas to fill entire web page: */
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make canvas fill entire document: */
|
|
||||||
canvas {
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.centered {
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
color: #f0f0f0;
|
|
||||||
font-size: 24px;
|
|
||||||
font-family: Ubuntu-Light, Helvetica, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------- */
|
|
||||||
/* Loading animation from https://loading.io/css/ */
|
|
||||||
.lds-dual-ring {
|
|
||||||
display: inline-block;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lds-dual-ring:after {
|
|
||||||
content: " ";
|
|
||||||
display: block;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
margin: 0px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 3px solid #fff;
|
|
||||||
border-color: #fff transparent #fff transparent;
|
|
||||||
animation: lds-dual-ring 1.2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes lds-dual-ring {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<!-- The WASM code will resize the canvas dynamically -->
|
|
||||||
<!-- the id is hardcoded in main.rs . so, make sure both match. -->
|
|
||||||
<canvas id="the_canvas_id"></canvas>
|
|
||||||
|
|
||||||
<!-- the loading spinner will be removed in main.rs -->
|
|
||||||
<div class="centered" id="loading_text">
|
|
||||||
<noscript>You need javascript to use this website</noscript>
|
|
||||||
<p style="font-size: 16px">Loading…</p>
|
|
||||||
<div class="lds-dual-ring"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--Register Service Worker. this will cache the wasm / js scripts for offline use (for PWA functionality). -->
|
|
||||||
<!-- Force refresh (Ctrl + F5) to load the latest files instead of cached files -->
|
|
||||||
<script>
|
|
||||||
// We disable caching during development so that we always view the latest version.
|
|
||||||
if (
|
|
||||||
"serviceWorker" in navigator &&
|
|
||||||
window.location.hash !== "#dev"
|
|
||||||
) {
|
|
||||||
window.addEventListener("load", function () {
|
|
||||||
navigator.serviceWorker.register("sw.js");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
<!-- Powered by egui: https://github.com/emilk/egui/ -->
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
use egui::Frame;
|
|
||||||
use egui_tiles::Tree;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
FORCE_REDRAW_DELAY,
|
|
||||||
app::{AppState, windows::WindowWrapper},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(default)] // if we add new fields, give them default values when deserializing old state
|
|
||||||
pub struct TemplateApp {
|
|
||||||
// tab: Tab,
|
|
||||||
state: AppState,
|
|
||||||
tree: Tree<WindowWrapper>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TemplateApp {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
state: AppState::default(),
|
|
||||||
tree: egui_tiles::Tree::new_horizontal("tree_root", Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TemplateApp {
|
|
||||||
/// Called once before the first frame.
|
|
||||||
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
|
||||||
// This is also where you can customize the look and feel of egui using
|
|
||||||
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
|
|
||||||
|
|
||||||
// Load previous app state (if any).
|
|
||||||
// Note that you must enable the `persistence` feature for this to work.
|
|
||||||
if let Some(storage) = cc.storage {
|
|
||||||
eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl eframe::App for TemplateApp {
|
|
||||||
/// Called by the framework to save state before shutdown.
|
|
||||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
|
||||||
eframe::set_value(storage, eframe::APP_KEY, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called each time the UI needs repainting, which may be many times per second.
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
||||||
ctx.plugin_or_default::<egui_async::EguiAsyncPlugin>();
|
|
||||||
|
|
||||||
if !self.state.auth.logged_in() {
|
|
||||||
egui::CentralPanel::default()
|
|
||||||
.frame(Frame::central_panel(&ctx.style()).inner_margin(0))
|
|
||||||
.show(ctx, |ui| self.state.auth.update(ui));
|
|
||||||
} else {
|
|
||||||
egui::TopBottomPanel::top("tab_panel").show(ctx, |ui| {
|
|
||||||
// The top panel is often a good place for a menu bar:
|
|
||||||
|
|
||||||
egui::MenuBar::new().ui(ui, |ui| {
|
|
||||||
ui.menu_button("File", |ui| {
|
|
||||||
ui.label("File");
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.menu_button("View", |ui| {
|
|
||||||
ui.label("View");
|
|
||||||
|
|
||||||
self.state.labels(&mut self.tree, ui);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
egui::widgets::global_theme_preference_switch(ui);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ui.style
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
egui::TopBottomPanel::bottom("tab_panel").show(ctx, |ui| {
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
ui.label(format!("UnShell UI {}", env!("CARGO_PKG_VERSION")));
|
|
||||||
egui::warn_if_debug_build(ui);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
egui::CentralPanel::default()
|
|
||||||
.frame(Frame::central_panel(&ctx.style()).inner_margin(0))
|
|
||||||
.show(ctx, |ui| self.tree.ui(&mut self.state, ui));
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.request_repaint_after(FORCE_REDRAW_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
mod app;
|
|
||||||
mod windows;
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
app::windows::WindowWrapper, auth::Auth, config::Config, flowchart::FlowChart,
|
|
||||||
interface::InterfaceWindow, log_viewer::LogViewer, payload_config::PayloadConfig,
|
|
||||||
};
|
|
||||||
pub use app::TemplateApp;
|
|
||||||
use egui_tiles::{TileId, Tree};
|
|
||||||
|
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct AppState {
|
|
||||||
pub auth: Auth,
|
|
||||||
|
|
||||||
pub open_windows: HashSet<AppWindow>,
|
|
||||||
|
|
||||||
pub flowchart: FlowChart,
|
|
||||||
pub config: Config,
|
|
||||||
pub payload_config: PayloadConfig,
|
|
||||||
pub log_viewer: LogViewer,
|
|
||||||
pub interface: InterfaceWindow,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppState {
|
|
||||||
pub fn labels(&mut self, tree: &mut Tree<WindowWrapper>, ui: &mut egui::Ui) {
|
|
||||||
for (_, (key, name)) in (vec![
|
|
||||||
(AppWindow::Flowchart, "Flowchart"),
|
|
||||||
(AppWindow::PayloadConfig, "Payload Config"),
|
|
||||||
(AppWindow::Config, "Config"),
|
|
||||||
(AppWindow::LogViewer, "Log Viewer"),
|
|
||||||
(AppWindow::InterfaceWindow, "Tree Test"),
|
|
||||||
])
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
let enabled = self.open_windows.contains(&key);
|
|
||||||
|
|
||||||
if ui.selectable_label(enabled, *name).clicked() {
|
|
||||||
if enabled {
|
|
||||||
self.close_window(tree, key);
|
|
||||||
} else {
|
|
||||||
self.open_window(tree, key, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_window(&mut self, tree: &mut Tree<WindowWrapper>, key: &AppWindow) {
|
|
||||||
match Self::find_pane_id(*key, tree) {
|
|
||||||
Some(tid) => {
|
|
||||||
let tid = tid.clone();
|
|
||||||
tree.remove_recursively(tid);
|
|
||||||
tree.tiles.remove(tid);
|
|
||||||
self.open_windows.remove(&key);
|
|
||||||
}
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_window(&mut self, tree: &mut Tree<WindowWrapper>, key: &AppWindow, name: &str) {
|
|
||||||
let tid = tree.tiles.insert_pane(WindowWrapper {
|
|
||||||
name: name.to_string(),
|
|
||||||
window: *key,
|
|
||||||
});
|
|
||||||
|
|
||||||
match self.open_windows.len() {
|
|
||||||
0 => {
|
|
||||||
tree.root = Some(tid);
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
let old_root = tree.root.unwrap();
|
|
||||||
let tab_id = tree.tiles.insert_tab_tile(vec![old_root, tid]);
|
|
||||||
tree.root = Some(tab_id);
|
|
||||||
tree.make_active(|t, _| t == tid);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let root = tree.root().unwrap();
|
|
||||||
let n = tree.tiles.get_container(root).unwrap().num_children();
|
|
||||||
tree.move_tile_to_container(tid, tree.root.unwrap().clone(), n, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.open_windows.insert(key.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_pane_id(window_type: AppWindow, tree: &Tree<WindowWrapper>) -> Option<&TileId> {
|
|
||||||
for (tid, window) in tree.tiles.iter() {
|
|
||||||
match window {
|
|
||||||
egui_tiles::Tile::Pane(pane) => {
|
|
||||||
if pane.window == window_type {
|
|
||||||
return Some(tid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
egui_tiles::Tile::Container(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, serde::Deserialize, serde::Serialize, PartialEq, Eq, Hash)]
|
|
||||||
pub enum AppWindow {
|
|
||||||
Flowchart,
|
|
||||||
Config,
|
|
||||||
PayloadConfig,
|
|
||||||
LogViewer,
|
|
||||||
InterfaceWindow,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppWindow {
|
|
||||||
fn update(&self, state: &mut AppState, ui: &mut egui::Ui) {
|
|
||||||
match self {
|
|
||||||
AppWindow::Flowchart => state.flowchart.paint(ui),
|
|
||||||
AppWindow::Config => state.config.update(&mut state.auth, ui),
|
|
||||||
AppWindow::PayloadConfig => state.payload_config.update(ui),
|
|
||||||
AppWindow::LogViewer => state.log_viewer.update(&mut state.auth, ui),
|
|
||||||
AppWindow::InterfaceWindow => state.interface.update(&mut state.auth, ui),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_title_buttons(&self, state: &mut AppState, ui: &mut egui::Ui) {
|
|
||||||
match self {
|
|
||||||
AppWindow::Flowchart => {
|
|
||||||
state.flowchart.titlebar_buttons(ui);
|
|
||||||
}
|
|
||||||
AppWindow::Config => {
|
|
||||||
state.config.titlebar_buttons(ui);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
ui.label("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
use egui::Rect;
|
|
||||||
|
|
||||||
use crate::app::{AppState, AppWindow};
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct WindowWrapper {
|
|
||||||
pub name: String,
|
|
||||||
pub window: AppWindow,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl egui_tiles::Behavior<WindowWrapper> for AppState {
|
|
||||||
fn tab_title_for_pane(&mut self, pane: &WindowWrapper) -> egui::WidgetText {
|
|
||||||
format!("{}", pane.name).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pane_ui(
|
|
||||||
&mut self,
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
_tile_id: egui_tiles::TileId,
|
|
||||||
pane: &mut WindowWrapper,
|
|
||||||
) -> egui_tiles::UiResponse {
|
|
||||||
let mut ret = egui_tiles::UiResponse::None;
|
|
||||||
|
|
||||||
let mut rect = Rect::NOTHING;
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
rect = ui.max_rect();
|
|
||||||
|
|
||||||
let bg_color = ui.style().visuals.extreme_bg_color;
|
|
||||||
|
|
||||||
ui.painter().rect_filled(rect, 0.0, bg_color);
|
|
||||||
|
|
||||||
ui.vertical_centered(|ui| {
|
|
||||||
ui.label(&pane.name);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut open_space = Rect::NOTHING;
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
ui.allocate_ui_at_rect(rect, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
pane.window.render_title_buttons(self, ui);
|
|
||||||
open_space = ui.available_rect_before_wrap();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let drag_sense = ui.interact(
|
|
||||||
open_space,
|
|
||||||
ui.id().with(&format!("Pane_{}_sense", pane.name)),
|
|
||||||
egui::Sense::drag(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if drag_sense.drag_started() {
|
|
||||||
ret = egui_tiles::UiResponse::DragStarted;
|
|
||||||
}
|
|
||||||
if drag_sense.hovered() {
|
|
||||||
ui.ctx().set_cursor_icon(egui::CursorIcon::Grab);
|
|
||||||
}
|
|
||||||
|
|
||||||
pane.window.update(self, ui);
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_tab_closable(
|
|
||||||
&self,
|
|
||||||
tiles: &egui_tiles::Tiles<WindowWrapper>,
|
|
||||||
tile_id: egui_tiles::TileId,
|
|
||||||
) -> bool {
|
|
||||||
match tiles.get(tile_id).unwrap() {
|
|
||||||
egui_tiles::Tile::Pane(_) => true,
|
|
||||||
egui_tiles::Tile::Container(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_tab_close(
|
|
||||||
&mut self,
|
|
||||||
tiles: &mut egui_tiles::Tiles<WindowWrapper>,
|
|
||||||
tile_id: egui_tiles::TileId,
|
|
||||||
) -> bool {
|
|
||||||
match tiles.get(tile_id).unwrap() {
|
|
||||||
egui_tiles::Tile::Pane(pane) => self.open_windows.remove(&pane.window),
|
|
||||||
egui_tiles::Tile::Container(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tab_bar_color(&self, visuals: &egui::Visuals) -> egui::Color32 {
|
|
||||||
visuals.panel_fill // same as the tab contents
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
use serde::de::DeserializeOwned;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use web_sys::js_sys::Promise;
|
|
||||||
|
|
||||||
pub struct PromiseWrapper<T> {
|
|
||||||
state: Rc<RefCell<PromiseState<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PromiseState<T> {
|
|
||||||
Pending,
|
|
||||||
Resolved(T),
|
|
||||||
Rejected(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static> PromiseWrapper<T>
|
|
||||||
where
|
|
||||||
T: DeserializeOwned,
|
|
||||||
{
|
|
||||||
/// Create a new PromiseWrapper from a JavaScript Promise
|
|
||||||
/// The promise should resolve to a Response object (from fetch)
|
|
||||||
pub fn new(promise: Promise) -> Self {
|
|
||||||
let state = Rc::new(RefCell::new(PromiseState::Pending));
|
|
||||||
let state_clone = state.clone();
|
|
||||||
let state_clone2 = state.clone();
|
|
||||||
|
|
||||||
// Success callback
|
|
||||||
let success = Closure::once(move |value: JsValue| {
|
|
||||||
if let Ok(response) = value.dyn_into::<web_sys::Response>() {
|
|
||||||
if let Ok(json_promise) = response.json() {
|
|
||||||
let state_inner = state_clone.clone();
|
|
||||||
|
|
||||||
let json_success = Closure::once(move |json_value: JsValue| {
|
|
||||||
*state_inner.borrow_mut() = if let Some(body) = json_value.as_string() {
|
|
||||||
match serde_json::from_str(&body) {
|
|
||||||
Ok(data) => PromiseState::Resolved(data),
|
|
||||||
Err(e) => PromiseState::Rejected(format!("{:?}", e)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PromiseState::Rejected(format!("Response was not a string"))
|
|
||||||
};
|
|
||||||
|
|
||||||
match serde_wasm_bindgen::from_value::<T>(json_value) {
|
|
||||||
Ok(data) => *state_inner.borrow_mut() = PromiseState::Resolved(data),
|
|
||||||
Err(e) => {
|
|
||||||
*state_inner.borrow_mut() =
|
|
||||||
PromiseState::Rejected(format!("{:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let _ = json_promise.then(&json_success);
|
|
||||||
json_success.forget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Error callback
|
|
||||||
let error = Closure::once(move |err: JsValue| {
|
|
||||||
*state_clone2.borrow_mut() = PromiseState::Rejected(
|
|
||||||
err.as_string()
|
|
||||||
.unwrap_or_else(|| "Unknown error".to_string()),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let _ = promise.then2(&success, &error);
|
|
||||||
success.forget();
|
|
||||||
error.forget();
|
|
||||||
|
|
||||||
Self { state }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Poll the promise to check if it has completed
|
|
||||||
/// Returns Some(T) if resolved successfully, None if still pending or rejected
|
|
||||||
/// This is a lightweight check that's safe to call every frame
|
|
||||||
#[inline]
|
|
||||||
pub fn poll(&self) -> Option<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
match &*self.state.borrow() {
|
|
||||||
PromiseState::Resolved(value) => Some(value.clone()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if the promise is still pending (lightweight)
|
|
||||||
#[inline]
|
|
||||||
pub fn is_pending(&self) -> bool {
|
|
||||||
matches!(&*self.state.borrow(), PromiseState::Pending)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if the promise was rejected (lightweight)
|
|
||||||
#[inline]
|
|
||||||
pub fn is_rejected(&self) -> bool {
|
|
||||||
matches!(&*self.state.borrow(), PromiseState::Rejected(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the error message if rejected
|
|
||||||
pub fn error(&self) -> Option<String> {
|
|
||||||
match &*self.state.borrow() {
|
|
||||||
PromiseState::Rejected(err) => Some(err.clone()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
use egui::{Align2, Area, Color32, Frame, Order, Sense, UiKind, Vec2, mutex::Mutex};
|
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
|
||||||
use serde_json::json;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use wasm_bindgen::prelude::Closure;
|
|
||||||
|
|
||||||
use unshell::Result;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Auth {
|
|
||||||
// Auth Stuff
|
|
||||||
token: Option<Token>,
|
|
||||||
#[serde(skip)]
|
|
||||||
auth_state: Arc<Mutex<AuthState>>,
|
|
||||||
|
|
||||||
// UI Stuff
|
|
||||||
username: String,
|
|
||||||
#[serde(skip)]
|
|
||||||
password: String,
|
|
||||||
show_password: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
|
|
||||||
struct Token {
|
|
||||||
expiration: u128,
|
|
||||||
token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
enum AuthState {
|
|
||||||
Unset,
|
|
||||||
NotLoggedIn,
|
|
||||||
RequestSent,
|
|
||||||
Authorised(Token),
|
|
||||||
Error(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AuthState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Unset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Auth {
|
|
||||||
/// Refresh the authentication state
|
|
||||||
pub fn logged_in(&mut self) -> bool {
|
|
||||||
match (self.token.is_some(), &*self.auth_state.lock()) {
|
|
||||||
// The client is actually authorized
|
|
||||||
(true, AuthState::Authorised(_)) => true,
|
|
||||||
|
|
||||||
// If the user has just reloaded the session,
|
|
||||||
// the AuthState is not automatically set by any other process
|
|
||||||
(true, AuthState::Unset) => true,
|
|
||||||
|
|
||||||
// If the authentication state has been updated to unauthorized, delete the token
|
|
||||||
(true, _) => {
|
|
||||||
self.token = None;
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the authentication state has been updated to authorized, set the token
|
|
||||||
(false, AuthState::Authorised(token)) => {
|
|
||||||
self.token = Some(token.clone());
|
|
||||||
|
|
||||||
// Also clear the password because it is bad to store it
|
|
||||||
self.password.clear();
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// The client is actually unauthorized
|
|
||||||
(false, _) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, ui: &mut egui::Ui) {
|
|
||||||
Area::new("Auth".into())
|
|
||||||
.kind(UiKind::Modal)
|
|
||||||
.sense(Sense::hover())
|
|
||||||
.anchor(Align2::CENTER_CENTER, Vec2::ZERO)
|
|
||||||
.order(Order::Foreground)
|
|
||||||
.interactable(true)
|
|
||||||
.show(ui.ctx(), |ui| {
|
|
||||||
Frame::popup(ui.style()).show(ui, |ui| {
|
|
||||||
ui.heading("UnShell Login");
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Username");
|
|
||||||
ui.text_edit_singleline(&mut self.username);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Password");
|
|
||||||
let _ = ui.add(
|
|
||||||
// [ui.available_width(), 24.],
|
|
||||||
egui::TextEdit::singleline(&mut self.password)
|
|
||||||
.password(!self.show_password),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.show_password = ui.button("Show").is_pointer_button_down_on();
|
|
||||||
|
|
||||||
// ui.toggle_value(&mut self.show_password, "Show");
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
let (show_spinner, err_text) = match *self.auth_state.lock() {
|
|
||||||
AuthState::Error(ref e) => {
|
|
||||||
// self.login_button(ui);
|
|
||||||
(false, e.clone())
|
|
||||||
}
|
|
||||||
AuthState::RequestSent => (true, "".into()),
|
|
||||||
_ => (false, "".into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !show_spinner {
|
|
||||||
if ui.button("Login").clicked() {
|
|
||||||
let state = self.auth_state.clone();
|
|
||||||
|
|
||||||
crate::httpPost(
|
|
||||||
"/api/auth",
|
|
||||||
&json!({
|
|
||||||
"username": self.username.clone(),
|
|
||||||
"password": self.password.clone()
|
|
||||||
})
|
|
||||||
.to_string(),
|
|
||||||
Closure::once_into_js(move |ok: bool, response: String| {
|
|
||||||
*(state.lock()) = if ok {
|
|
||||||
if let Ok(token) =
|
|
||||||
serde_json::from_str::<Token>(&response)
|
|
||||||
{
|
|
||||||
AuthState::Authorised(token)
|
|
||||||
} else {
|
|
||||||
AuthState::Error("Malformed Response".into())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AuthState::Error(response)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
*(self.auth_state.lock()) = AuthState::RequestSent;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui.spinner();
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.colored_label(Color32::RED, err_text);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<R, F>(&self, path: &str, ret: F) -> Result<()>
|
|
||||||
where
|
|
||||||
F: FnOnce(R) + 'static,
|
|
||||||
R: DeserializeOwned,
|
|
||||||
{
|
|
||||||
if let Some(ref token) = self.token {
|
|
||||||
let state = self.auth_state.clone();
|
|
||||||
crate::httpGetAuth(
|
|
||||||
path,
|
|
||||||
format!("Bearer {}", token.token),
|
|
||||||
Closure::once_into_js(move |ok: bool, response: String| {
|
|
||||||
if ok {
|
|
||||||
if let Ok(value) = serde_json::from_str::<R>(&response) {
|
|
||||||
ret(value)
|
|
||||||
} else {
|
|
||||||
*(state.lock()) = AuthState::Error("Malformed Response".into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*(state.lock()) = AuthState::Error(response);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn post<R, T, F>(&self, path: &str, data: &T, ret: F) -> Result<()>
|
|
||||||
where
|
|
||||||
R: DeserializeOwned,
|
|
||||||
T: Serialize,
|
|
||||||
F: FnOnce(R) + 'static,
|
|
||||||
{
|
|
||||||
if let Some(ref token) = self.token {
|
|
||||||
let state = self.auth_state.clone();
|
|
||||||
crate::httpPostAuth(
|
|
||||||
path,
|
|
||||||
format!("Bearer {}", token.token),
|
|
||||||
&serde_json::to_string(data)?,
|
|
||||||
Closure::once_into_js(move |ok: bool, response: String| {
|
|
||||||
if ok {
|
|
||||||
if let Ok(value) = serde_json::from_str::<R>(&response) {
|
|
||||||
ret(value)
|
|
||||||
} else {
|
|
||||||
*(state.lock()) = AuthState::Error("Malformed Response".into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*(state.lock()) = AuthState::Error(response);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Auth {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
token: Default::default(),
|
|
||||||
auth_state: Arc::new(Mutex::new(AuthState::NotLoggedIn)),
|
|
||||||
username: Default::default(),
|
|
||||||
password: Default::default(),
|
|
||||||
show_password: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
impl Auth {
|
|
||||||
// pub fn get_test(url: String) -> Promise {
|
|
||||||
// let fut = Self::get_async(&url);
|
|
||||||
|
|
||||||
// // wasm_bindgen_futures::(fut)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn get_async<R>(&self, url: &str) -> PromiseWrapper<R>
|
|
||||||
// where
|
|
||||||
// // F: FnOnce(R) + 'static,
|
|
||||||
// R: DeserializeOwned + 'static,
|
|
||||||
// {
|
|
||||||
// let token = self.token.as_ref().unwrap();
|
|
||||||
|
|
||||||
// let opts = RequestInit::new();
|
|
||||||
// opts.set_method("GET");
|
|
||||||
|
|
||||||
// let request = Request::new_with_str_and_init(url, &opts).unwrap();
|
|
||||||
|
|
||||||
// let headers = request.headers();
|
|
||||||
|
|
||||||
// headers.set("content-type", "application/json").unwrap();
|
|
||||||
|
|
||||||
// headers
|
|
||||||
// .set("Authorization", &format!("Bearer {}", token.token))
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
// let window = web_sys::window().unwrap();
|
|
||||||
// let promise = window.fetch_with_request(&request);
|
|
||||||
|
|
||||||
// // wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
// // let resp_value = JsFuture::from(window.fetch_with_request(&request))
|
|
||||||
// // .await
|
|
||||||
// // .unwrap();
|
|
||||||
|
|
||||||
// // // `resp_value` is a `Response` object.
|
|
||||||
// // assert!(resp_value.is_instance_of::<Response>());
|
|
||||||
// // let resp: Response = resp_value.dyn_into().unwrap();
|
|
||||||
|
|
||||||
// // // Convert this other `Promise` into a rust `Future`.
|
|
||||||
// // if let Ok(json) = resp.json() {
|
|
||||||
// // if let Ok(json) = JsFuture::from(json).await {
|
|
||||||
// // crate::log(&format!("{json:?}"));
|
|
||||||
// // // let json = ;
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// // // crate::log(text);
|
|
||||||
// // // Any follow-up work here
|
|
||||||
// // });
|
|
||||||
|
|
||||||
// PromiseWrapper::new(promise)
|
|
||||||
|
|
||||||
// // Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn get_async_callback<R, F>(&self, url: &str, callback: F) -> Result<()>
|
|
||||||
// where
|
|
||||||
// F: FnOnce(R) + 'static,
|
|
||||||
// R: DeserializeOwned + 'static,
|
|
||||||
// {
|
|
||||||
// if self.token.is_none() {
|
|
||||||
// return Err(ModuleError::Error("Not authenticated".into()));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let token = self.token.clone().unwrap();
|
|
||||||
|
|
||||||
// let url = url.to_string();
|
|
||||||
|
|
||||||
// let state_clone = self.auth_state.clone();
|
|
||||||
|
|
||||||
// wasm_bindgen_futures::future_to_promise(async move {
|
|
||||||
// let result = Self::get_async(&url, &token).await;
|
|
||||||
|
|
||||||
// match result {
|
|
||||||
// Ok(result) => callback(result),
|
|
||||||
// Err(err) => (*state_clone.lock()) = AuthState::Error(err.into()),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(JsValue::NULL)
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async fn get_async<R>(url: &str, token: &Token) -> Result<R>
|
|
||||||
// where
|
|
||||||
// R: DeserializeOwned + 'static,
|
|
||||||
// {
|
|
||||||
// let res = reqwest::Client::new()
|
|
||||||
// .get(format!("http://localhost:8080{url}"))
|
|
||||||
// .bearer_auth(&token.token)
|
|
||||||
// .send()
|
|
||||||
// .await
|
|
||||||
// .map_err(|e| ModuleError::Error(e.to_string()))?;
|
|
||||||
|
|
||||||
// match res.error_for_status() {
|
|
||||||
// Ok(res) => res
|
|
||||||
// .json()
|
|
||||||
// .await
|
|
||||||
// .map_err(|e| ModuleError::Error(e.to_string())),
|
|
||||||
// Err(err) => Err(ModuleError::Error(format!(
|
|
||||||
// "Server returned error: {err:?}"
|
|
||||||
// ))),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // .json::<R>()
|
|
||||||
// // .await
|
|
||||||
// // .map_err(|e| ModuleError::Error(e.to_string()))?;
|
|
||||||
|
|
||||||
// // serde_json::from_str(&res).map_err(|e| ModuleError::SerdeJsonError(e.to_string()))
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use egui_extras::{Column, TableBuilder};
|
|
||||||
|
|
||||||
use crate::auth::Auth;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Config {
|
|
||||||
tree_option: String,
|
|
||||||
#[serde(skip)]
|
|
||||||
state: Arc<Mutex<ConfigState>>,
|
|
||||||
// trees: Arc<Mutex<Option<Vec<String>>>>,
|
|
||||||
// #[serde(skip)]
|
|
||||||
// is_requesting: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ConfigState {
|
|
||||||
trees: Option<Vec<String>>,
|
|
||||||
tree_keys: Option<HashMap<String, String>>,
|
|
||||||
is_requesting: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
tree_option: "".into(),
|
|
||||||
state: Arc::new(Mutex::new(ConfigState {
|
|
||||||
trees: None,
|
|
||||||
tree_keys: None,
|
|
||||||
is_requesting: false,
|
|
||||||
})), // trees: Arc::new(Mutex::new(None)),
|
|
||||||
// is_requesting: Arc::new(AtomicBool::new(false)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn update(&mut self, auth: &mut Auth, ui: &mut egui::Ui) {
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
let tree_list_none = state_lock.trees.is_none();
|
|
||||||
let key_list_none = state_lock.tree_keys.is_none();
|
|
||||||
let is_requesting = state_lock.is_requesting;
|
|
||||||
|
|
||||||
if !tree_list_none
|
|
||||||
&& !state_lock
|
|
||||||
.trees
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.contains(&self.tree_option)
|
|
||||||
{
|
|
||||||
self.tree_option.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(state_lock);
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if tree_list_none && !is_requesting {
|
|
||||||
self.state.lock().unwrap().is_requesting = true;
|
|
||||||
let state_clone = self.state.clone();
|
|
||||||
|
|
||||||
auth.get("/api/trees", move |response: Vec<String>| {
|
|
||||||
let mut state_lock = state_clone.lock().unwrap();
|
|
||||||
state_lock.trees = Some(response);
|
|
||||||
state_lock.is_requesting = false;
|
|
||||||
drop(state_lock);
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
} else if tree_list_none && is_requesting {
|
|
||||||
ui.spinner();
|
|
||||||
}
|
|
||||||
|
|
||||||
// let state_lock = self.state.lock().unwrap();
|
|
||||||
// // This might have changed since the above api call
|
|
||||||
// tree_list_none = state_lock.trees.is_none();
|
|
||||||
|
|
||||||
if !self.tree_option.is_empty() && !tree_list_none {
|
|
||||||
// ui.horizontal(|ui| {
|
|
||||||
// ui.label(&format!("Tree: {}", self.tree_option));
|
|
||||||
// });
|
|
||||||
|
|
||||||
if key_list_none && !is_requesting {
|
|
||||||
self.state.lock().unwrap().is_requesting = true;
|
|
||||||
let state_clone = self.state.clone();
|
|
||||||
|
|
||||||
auth.get(
|
|
||||||
&format!("/api/values/{}", self.tree_option),
|
|
||||||
move |response: Result<HashMap<String, String>, String>| {
|
|
||||||
let mut state_lock = state_clone.lock().unwrap();
|
|
||||||
state_lock.tree_keys = Some(response.unwrap());
|
|
||||||
state_lock.is_requesting = false;
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else if key_list_none && is_requesting {
|
|
||||||
ui.spinner();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if !key_list_none {
|
|
||||||
// let body_text_size = TextStyle::Body.resolve(ui.style()).size;
|
|
||||||
// use egui_extras::{Size, StripBuilder};
|
|
||||||
// StripBuilder::new(ui)
|
|
||||||
// .size(Size::remainder().at_least(100.0)) // for the table
|
|
||||||
// .size(Size::exact(body_text_size))
|
|
||||||
// .vertical(|mut strip| {
|
|
||||||
// strip.cell(|ui| {
|
|
||||||
|
|
||||||
egui::ScrollArea::both().show(ui, |ui| {
|
|
||||||
let table = TableBuilder::new(ui)
|
|
||||||
.striped(true)
|
|
||||||
.resizable(true)
|
|
||||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
|
||||||
.column(Column::auto())
|
|
||||||
.column(Column::auto())
|
|
||||||
.min_scrolled_height(0.0)
|
|
||||||
.sense(egui::Sense::click());
|
|
||||||
|
|
||||||
table
|
|
||||||
.header(20., |mut header| {
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("key");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("value");
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.body(|mut body| {
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
let map = state_lock.tree_keys.as_ref().unwrap();
|
|
||||||
|
|
||||||
for (key, value) in (map).iter() {
|
|
||||||
// // let runtime = self.current_runtimes
|
|
||||||
|
|
||||||
body.row(18., |mut row| {
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(key);
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(value);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn titlebar_buttons(&mut self, ui: &mut egui::Ui) {
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
let mut tree_list_none = state_lock.trees.is_none();
|
|
||||||
|
|
||||||
drop(state_lock);
|
|
||||||
|
|
||||||
if ui.button("Refresh").clicked() {
|
|
||||||
let mut state_lock = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
(*state_lock).trees = None;
|
|
||||||
(*state_lock).tree_keys = None;
|
|
||||||
drop(state_lock);
|
|
||||||
|
|
||||||
tree_list_none = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tree_list_none {
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
let trees = state_lock.trees.as_ref().unwrap().clone();
|
|
||||||
drop(state_lock);
|
|
||||||
|
|
||||||
let before = &self.tree_option.clone();
|
|
||||||
egui::ComboBox::from_id_salt("Select Tree")
|
|
||||||
.selected_text(&self.tree_option)
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
for tree in trees {
|
|
||||||
ui.selectable_value(&mut self.tree_option, tree.clone(), tree);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if before.ne(&self.tree_option) {
|
|
||||||
(*self.state.lock().unwrap()).tree_keys = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
use egui::{TextStyle, Ui};
|
|
||||||
use egui_extras::{Column, TableBuilder};
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Config {
|
|
||||||
state: ConfigState,
|
|
||||||
current_payloads: Vec<PayloadConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
enum ConfigState {
|
|
||||||
Base,
|
|
||||||
NewConfig(PayloadConfig),
|
|
||||||
EditConfig(usize, PayloadConfig),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
state: ConfigState::Base,
|
|
||||||
current_payloads: vec![PayloadConfig {
|
|
||||||
name: "Test".to_string(),
|
|
||||||
components: vec!["server".to_string()],
|
|
||||||
// runtimes: Vec::new(),
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
// pub fn title(&self) -> &str {
|
|
||||||
// match self.state {
|
|
||||||
// ConfigState::Base => "Config",
|
|
||||||
// ConfigState::NewConfig(..) => "Config/New",
|
|
||||||
// ConfigState::EditConfig(..) => "Config/Edit",
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn update(&mut self, ui: &mut Ui) {
|
|
||||||
match &mut self.state {
|
|
||||||
ConfigState::Base => self.table_ui(ui),
|
|
||||||
ConfigState::EditConfig(_, config) => {
|
|
||||||
ui.heading("Edit Payload");
|
|
||||||
match Self::edit_ui(ui, config) {
|
|
||||||
Some(true) => {
|
|
||||||
if let ConfigState::EditConfig(n, config) =
|
|
||||||
std::mem::replace(&mut self.state, ConfigState::Base)
|
|
||||||
{
|
|
||||||
self.current_payloads[n] = config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(false) => {
|
|
||||||
self.state = ConfigState::Base;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigState::NewConfig(config) => {
|
|
||||||
ui.heading("Edit Payload");
|
|
||||||
match Self::edit_ui(ui, config) {
|
|
||||||
Some(true) => {
|
|
||||||
if let ConfigState::NewConfig(config) =
|
|
||||||
std::mem::replace(&mut self.state, ConfigState::Base)
|
|
||||||
{
|
|
||||||
self.current_payloads.push(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(false) => {
|
|
||||||
self.state = ConfigState::Base;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn table_ui(&mut self, ui: &mut Ui) {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.heading("Payloads");
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
if ui.button("New").clicked() {
|
|
||||||
self.state = ConfigState::NewConfig(PayloadConfig::new());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let body_text_size = TextStyle::Body.resolve(ui.style()).size;
|
|
||||||
use egui_extras::{Size, StripBuilder};
|
|
||||||
StripBuilder::new(ui)
|
|
||||||
.size(Size::remainder().at_least(100.0)) // for the table
|
|
||||||
.size(Size::exact(body_text_size)) // for the source code link
|
|
||||||
.vertical(|mut strip| {
|
|
||||||
strip.cell(|ui| {
|
|
||||||
egui::ScrollArea::horizontal().show(ui, |ui| {
|
|
||||||
let table = TableBuilder::new(ui)
|
|
||||||
.striped(true)
|
|
||||||
.resizable(true)
|
|
||||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
|
||||||
.column(Column::auto().resizable(false))
|
|
||||||
.column(Column::remainder())
|
|
||||||
.column(Column::remainder())
|
|
||||||
.column(Column::remainder())
|
|
||||||
.column(Column::auto().resizable(false))
|
|
||||||
.min_scrolled_height(0.0)
|
|
||||||
.sense(egui::Sense::click());
|
|
||||||
|
|
||||||
table
|
|
||||||
.header(20., |mut header| {
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("#");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Name");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Components");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Runtimes");
|
|
||||||
});
|
|
||||||
header.col(|_| {});
|
|
||||||
})
|
|
||||||
.body(|mut body| {
|
|
||||||
for i in 0..self.current_payloads.len() {
|
|
||||||
// let runtime = self.current_runtimes
|
|
||||||
|
|
||||||
body.row(18., |mut row| {
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(i.to_string());
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(self.current_payloads[i].name.clone());
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(format!(
|
|
||||||
"{:?}",
|
|
||||||
self.current_payloads[i].components.clone()
|
|
||||||
));
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label("A");
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
if ui.button("Edit").clicked() {
|
|
||||||
self.state = ConfigState::EditConfig(
|
|
||||||
i,
|
|
||||||
self.current_payloads[i].clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// if ui.button("Delete").clicked() {
|
|
||||||
// self.state = ConfigState::EditConfig(
|
|
||||||
// i,
|
|
||||||
// self.current_payloads[i].clone(),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
if row.response().clicked() {
|
|
||||||
self.state = ConfigState::EditConfig(
|
|
||||||
i,
|
|
||||||
self.current_payloads[i].clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn edit_ui(ui: &mut Ui, config: &mut PayloadConfig) -> Option<bool> {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name");
|
|
||||||
ui.text_edit_singleline(&mut config.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Components: ");
|
|
||||||
|
|
||||||
for component in vec!["client", "server"] {
|
|
||||||
let enabled = config
|
|
||||||
.components
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|s| s.1.eq(component));
|
|
||||||
if ui.selectable_label(enabled.is_some(), component).clicked() {
|
|
||||||
if let Some((i, _)) = enabled {
|
|
||||||
let _ = config.components.remove(i);
|
|
||||||
} else {
|
|
||||||
config.components.push(component.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut ret = None;
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ret = if ui.button("Back").clicked() {
|
|
||||||
Some(false)
|
|
||||||
} else if ui.button("Save").clicked() {
|
|
||||||
Some(true)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
// return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
struct PayloadConfig {
|
|
||||||
name: String,
|
|
||||||
components: Vec<String>,
|
|
||||||
// runtimes: Vec<RuntimeConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PayloadConfig {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
name: "New Payload".to_string(),
|
|
||||||
components: Vec::new(),
|
|
||||||
// runtimes: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
use egui::{Pos2, Rect, UiBuilder, Vec2};
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct DraggableContainer {
|
|
||||||
pub pos: egui::Vec2, // Offset from center of clip_rect
|
|
||||||
pub size: egui::Vec2,
|
|
||||||
|
|
||||||
is_dragging: bool,
|
|
||||||
drag_offset: egui::Vec2,
|
|
||||||
drag_id: String,
|
|
||||||
|
|
||||||
pub vel: egui::Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DraggableContainer {
|
|
||||||
// pub fn new(center_offset: egui::Vec2, size: egui::Vec2, id: usize) -> Self {
|
|
||||||
// Self {
|
|
||||||
// pos: center_offset,
|
|
||||||
// size,
|
|
||||||
// is_dragging: false,
|
|
||||||
// drag_offset: egui::Vec2::ZERO,
|
|
||||||
// drag_id: format!("flowchart_drag_area{}", id),
|
|
||||||
// vel: Vec2::ZERO,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn new_zero(id: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
pos: Vec2::ZERO,
|
|
||||||
size: Vec2 { x: 100., y: 100. },
|
|
||||||
is_dragging: false,
|
|
||||||
drag_offset: egui::Vec2::ZERO,
|
|
||||||
drag_id: format!("flowchart_drag_area{}", id),
|
|
||||||
vel: Vec2::ZERO,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show<R>(
|
|
||||||
&mut self,
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
add_contents: impl FnOnce(&mut egui::Ui, &Rect) -> R,
|
|
||||||
) -> R {
|
|
||||||
// Calculate center of the clip rect
|
|
||||||
let clip_center = Pos2::ZERO;
|
|
||||||
|
|
||||||
// Calculate actual position from center offset
|
|
||||||
let center_pos = clip_center + self.pos;
|
|
||||||
let pos = center_pos - self.size / 2.0; // Top-left corner from center
|
|
||||||
let rect = egui::Rect::from_min_size(pos, self.size);
|
|
||||||
|
|
||||||
// Handle dragging logic
|
|
||||||
let response = ui.interact(rect, ui.id().with(&self.drag_id), egui::Sense::drag());
|
|
||||||
|
|
||||||
// if response.secondary_clicked() {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
if response.drag_started() {
|
|
||||||
self.is_dragging = true;
|
|
||||||
if let Some(pointer_pos) = ui.input(|i| i.pointer.latest_pos()) {
|
|
||||||
let pointer_pos = ui
|
|
||||||
.ctx()
|
|
||||||
.layer_transform_from_global(ui.painter().layer_id())
|
|
||||||
.unwrap_or_default()
|
|
||||||
* pointer_pos;
|
|
||||||
self.drag_offset = center_pos - pointer_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.dragged() && self.is_dragging {
|
|
||||||
// Pointer code from https://github.com/emilk/egui/pull/7149
|
|
||||||
if let Some(pointer_pos) = ui.input(|i| i.pointer.latest_pos()) {
|
|
||||||
let pointer_pos = ui
|
|
||||||
.ctx()
|
|
||||||
.layer_transform_from_global(ui.painter().layer_id())
|
|
||||||
.unwrap_or_default()
|
|
||||||
* pointer_pos;
|
|
||||||
let new_center = pointer_pos + self.drag_offset;
|
|
||||||
self.pos = new_center - clip_center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.drag_stopped() {
|
|
||||||
self.is_dragging = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a child UI at the specified position
|
|
||||||
let mut child_ui = ui.new_child(UiBuilder::new().max_rect(rect));
|
|
||||||
|
|
||||||
// Add contents
|
|
||||||
add_contents(&mut child_ui, &rect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
use egui::{Color32, Painter, Pos2, Rect, Scene, Shape, Ui};
|
|
||||||
|
|
||||||
use crate::flowchart::CONNECTION_STROKE;
|
|
||||||
use crate::flowchart::GROUP_BORDER_MARGIN;
|
|
||||||
use crate::flowchart::ITERATIONS;
|
|
||||||
use crate::flowchart::RESOLUTION;
|
|
||||||
use crate::flowchart::container::DraggableContainer;
|
|
||||||
use crate::flowchart::group::convex_hull;
|
|
||||||
use crate::flowchart::{BG_STROKE, TARGET_LINE_GAP};
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
|
|
||||||
pub struct FlowChart {
|
|
||||||
scene_rect: Rect,
|
|
||||||
pub containers: Vec<DraggableContainer>,
|
|
||||||
pub connections: Vec<(usize, usize)>,
|
|
||||||
pub groups: Vec<Vec<usize>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FlowChart {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut this = Self {
|
|
||||||
scene_rect: Rect::ZERO,
|
|
||||||
containers: vec![
|
|
||||||
DraggableContainer::new_zero(0),
|
|
||||||
DraggableContainer::new_zero(1),
|
|
||||||
DraggableContainer::new_zero(2),
|
|
||||||
DraggableContainer::new_zero(3),
|
|
||||||
DraggableContainer::new_zero(4),
|
|
||||||
DraggableContainer::new_zero(5),
|
|
||||||
DraggableContainer::new_zero(6),
|
|
||||||
DraggableContainer::new_zero(7),
|
|
||||||
],
|
|
||||||
connections: vec![(0, 1), (1, 2), (1, 3), (1, 4), (3, 5), (3, 6), (3, 7)],
|
|
||||||
groups: vec![vec![1, 3, 5, 7]],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.arrange_circle();
|
|
||||||
this.arrange();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlowChart {
|
|
||||||
pub fn arrange(&mut self) {
|
|
||||||
for _ in 0..ITERATIONS {
|
|
||||||
self.force(RESOLUTION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint_bg(rect: &Rect, painter: &Painter) {
|
|
||||||
let h_start = (rect.min.x / TARGET_LINE_GAP).round() as i32;
|
|
||||||
let h_end = ((rect.min.x + rect.width()) / TARGET_LINE_GAP).round() as i32 + 1;
|
|
||||||
for n in h_start..h_end {
|
|
||||||
painter.vline(n as f32 * TARGET_LINE_GAP, rect.y_range(), BG_STROKE);
|
|
||||||
}
|
|
||||||
|
|
||||||
let v_start = (rect.min.y / TARGET_LINE_GAP).round() as i32;
|
|
||||||
let v_end = ((rect.min.y + rect.height()) / TARGET_LINE_GAP).round() as i32 + 1;
|
|
||||||
for n in v_start..v_end {
|
|
||||||
painter.hline(rect.x_range(), n as f32 * TARGET_LINE_GAP, BG_STROKE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint_groups(groups: &Vec<Vec<usize>>, containers: &Vec<DraggableContainer>, ui: &mut Ui) {
|
|
||||||
for group in groups {
|
|
||||||
let mut points = Vec::new();
|
|
||||||
|
|
||||||
for n in group {
|
|
||||||
let container = &containers[*n];
|
|
||||||
let pos = container.pos.to_pos2();
|
|
||||||
let size = container.size;
|
|
||||||
points.append(&mut vec![
|
|
||||||
Pos2 {
|
|
||||||
x: pos.x - size.x / 2. - GROUP_BORDER_MARGIN,
|
|
||||||
y: pos.y - size.x / 2. - GROUP_BORDER_MARGIN,
|
|
||||||
},
|
|
||||||
Pos2 {
|
|
||||||
x: pos.x + size.x / 2. + GROUP_BORDER_MARGIN,
|
|
||||||
y: pos.y - size.y / 2. - GROUP_BORDER_MARGIN,
|
|
||||||
},
|
|
||||||
Pos2 {
|
|
||||||
x: pos.x - size.x / 2. - GROUP_BORDER_MARGIN,
|
|
||||||
y: pos.y + size.y / 2. + GROUP_BORDER_MARGIN,
|
|
||||||
},
|
|
||||||
Pos2 {
|
|
||||||
x: pos.x + size.x / 2. + GROUP_BORDER_MARGIN,
|
|
||||||
y: pos.y + size.y / 2. + GROUP_BORDER_MARGIN,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let points = convex_hull(&points);
|
|
||||||
|
|
||||||
ui.painter().add(Shape::convex_polygon(
|
|
||||||
points,
|
|
||||||
Color32::DEBUG_COLOR,
|
|
||||||
BG_STROKE,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn paint(&mut self, ui: &mut Ui) {
|
|
||||||
let scene = Scene::new()
|
|
||||||
// .max_inner_size([350.0, 1000.0])
|
|
||||||
.zoom_range(0.1..=2.0);
|
|
||||||
|
|
||||||
let containers = &mut self.containers;
|
|
||||||
let groups = &self.groups;
|
|
||||||
|
|
||||||
let mut inner_rect = Rect::NAN;
|
|
||||||
let rect = &self.scene_rect.clone();
|
|
||||||
|
|
||||||
let response = scene
|
|
||||||
.show(ui, &mut self.scene_rect, |mut ui| {
|
|
||||||
Self::paint_bg(rect, ui.painter());
|
|
||||||
Self::paint_groups(groups, containers, &mut ui);
|
|
||||||
|
|
||||||
for (a, b) in &self.connections {
|
|
||||||
ui.painter().line_segment(
|
|
||||||
[containers[*a].pos.to_pos2(), containers[*b].pos.to_pos2()],
|
|
||||||
CONNECTION_STROKE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for container in containers {
|
|
||||||
container.show(&mut ui, |ui, rect| {
|
|
||||||
ui.painter().rect(
|
|
||||||
// ui.top
|
|
||||||
*rect,
|
|
||||||
0.,
|
|
||||||
Color32::PURPLE,
|
|
||||||
BG_STROKE,
|
|
||||||
egui::StrokeKind::Outside,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
inner_rect = ui.min_rect();
|
|
||||||
})
|
|
||||||
.response;
|
|
||||||
|
|
||||||
if response.double_clicked() {
|
|
||||||
self.scene_rect = inner_rect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn titlebar_buttons(&mut self, ui: &mut egui::Ui) {
|
|
||||||
if ui.button("Arrange").clicked() {
|
|
||||||
self.arrange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
use std::f32::consts::TAU;
|
|
||||||
|
|
||||||
use egui::Vec2;
|
|
||||||
|
|
||||||
use crate::flowchart::{
|
|
||||||
ATTRACTION_STRENGTH, CENTER_ATTRACTION_STRENGTH, DAMPING, FlowChart, GROUP_ATTRACTION_STRENGTH,
|
|
||||||
REPULSION_STRENGTH, REST_LENGTH,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn normalize(v: &Vec2) -> Vec2 {
|
|
||||||
let len = v.length();
|
|
||||||
if len > 0.0 {
|
|
||||||
Vec2 {
|
|
||||||
x: v.x / len,
|
|
||||||
y: v.y / len,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Vec2 { x: 0.0, y: 0.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlowChart {
|
|
||||||
pub fn force(&mut self, delta_time: f32) {
|
|
||||||
let num_nodes = self.containers.len();
|
|
||||||
let mut forces = vec![Vec2::new(0.0, 0.0); num_nodes];
|
|
||||||
|
|
||||||
// Calculate repulsive forces between all nodes
|
|
||||||
for i in 0..num_nodes {
|
|
||||||
for j in (i + 1)..num_nodes {
|
|
||||||
let diff = self.containers[i].pos - self.containers[j].pos;
|
|
||||||
let dist = diff.length().max(0.1); // Prevent division by zero
|
|
||||||
let force = normalize(&diff) * (REPULSION_STRENGTH / (dist * dist));
|
|
||||||
|
|
||||||
forces[i] = forces[i] + force;
|
|
||||||
forces[j] = forces[j] + (force * -1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate attractive forces along connections
|
|
||||||
for &(i, j) in &self.connections {
|
|
||||||
let diff = self.containers[j].pos - self.containers[i].pos;
|
|
||||||
let dist = diff.length();
|
|
||||||
let displacement = dist - REST_LENGTH;
|
|
||||||
let force = normalize(&diff) * (displacement * ATTRACTION_STRENGTH);
|
|
||||||
|
|
||||||
forces[i] = forces[i] + force;
|
|
||||||
forces[j] = forces[j] + (force * -1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply force to center
|
|
||||||
for i in 0..num_nodes {
|
|
||||||
let diff = self.containers[i].pos;
|
|
||||||
let dist = diff.length();
|
|
||||||
let displacement = dist - REST_LENGTH;
|
|
||||||
let force = normalize(&diff) * (displacement * CENTER_ATTRACTION_STRENGTH);
|
|
||||||
|
|
||||||
forces[i] = forces[i] + force * -1.;
|
|
||||||
}
|
|
||||||
|
|
||||||
let group_avg = &self
|
|
||||||
.groups
|
|
||||||
.iter()
|
|
||||||
.map(|group| {
|
|
||||||
let mut sum = Vec2::ZERO;
|
|
||||||
for n in group {
|
|
||||||
sum += self.containers[*n].pos;
|
|
||||||
}
|
|
||||||
sum / group.len() as f32
|
|
||||||
})
|
|
||||||
.collect::<Vec<Vec2>>();
|
|
||||||
|
|
||||||
for (group, group_avg) in self.groups.iter().zip(group_avg) {
|
|
||||||
for i in 0..num_nodes {
|
|
||||||
let diff = self.containers[i].pos - *group_avg;
|
|
||||||
let dist = diff.length();
|
|
||||||
let displacement = dist - REST_LENGTH;
|
|
||||||
let force = normalize(&diff) * (displacement * GROUP_ATTRACTION_STRENGTH);
|
|
||||||
|
|
||||||
if group.contains(&i) {
|
|
||||||
forces[i] = forces[i] + force * -1.;
|
|
||||||
} else {
|
|
||||||
forces[i] = forces[i] + force;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update velocities and positions
|
|
||||||
for i in 0..num_nodes {
|
|
||||||
let c = &mut self.containers[i];
|
|
||||||
c.vel = (c.vel + forces[i] * delta_time) * DAMPING;
|
|
||||||
c.pos += c.vel * delta_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arrange_circle(&mut self) {
|
|
||||||
let node_count = self.containers.len() as f32;
|
|
||||||
for (i, m) in self.containers.iter_mut().enumerate() {
|
|
||||||
let ang = -(i as f32 / node_count) * TAU;
|
|
||||||
m.pos = Vec2 {
|
|
||||||
x: 300. * ang.sin(),
|
|
||||||
y: 300. * ang.cos(),
|
|
||||||
};
|
|
||||||
m.vel = Vec2::ZERO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
use egui::Pos2;
|
|
||||||
|
|
||||||
/// Calculate the convex hull of a set of points using Graham scan
|
|
||||||
pub fn convex_hull(points: &[Pos2]) -> Vec<Pos2> {
|
|
||||||
if points.len() < 3 {
|
|
||||||
return points.to_vec();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pts = points.to_vec();
|
|
||||||
|
|
||||||
// Find the point with lowest y-coordinate (and leftmost if tie)
|
|
||||||
let start_idx = pts
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.min_by(|(_, a), (_, b)| {
|
|
||||||
a.y.partial_cmp(&b.y)
|
|
||||||
.unwrap()
|
|
||||||
.then(a.x.partial_cmp(&b.x).unwrap())
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
|
|
||||||
pts.swap(0, start_idx);
|
|
||||||
let start = pts[0];
|
|
||||||
|
|
||||||
// Sort points by polar angle with respect to start point
|
|
||||||
pts[1..].sort_by(|a, b| {
|
|
||||||
let angle_a = polar_angle_to(&start, a);
|
|
||||||
let angle_b = polar_angle_to(&start, b);
|
|
||||||
angle_a.partial_cmp(&angle_b).unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build convex hull
|
|
||||||
let mut hull = Vec::new();
|
|
||||||
hull.push(pts[0]);
|
|
||||||
hull.push(pts[1]);
|
|
||||||
|
|
||||||
for i in 2..pts.len() {
|
|
||||||
while hull.len() > 1
|
|
||||||
&& cross_product(&hull[hull.len() - 2], &hull[hull.len() - 1], &pts[i]) <= 0.0
|
|
||||||
{
|
|
||||||
hull.pop();
|
|
||||||
}
|
|
||||||
hull.push(pts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
hull
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate cross product of vectors (self->p2) and (self->p3)
|
|
||||||
/// Positive if counter-clockwise, negative if clockwise, zero if collinear
|
|
||||||
fn cross_product(p1: &Pos2, p2: &Pos2, p3: &Pos2) -> f32 {
|
|
||||||
(p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate polar angle from self to other point
|
|
||||||
fn polar_angle_to(a: &Pos2, b: &Pos2) -> f32 {
|
|
||||||
(b.y - a.y).atan2(b.x - a.x)
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
mod container;
|
|
||||||
mod flowchart;
|
|
||||||
mod force;
|
|
||||||
mod group;
|
|
||||||
|
|
||||||
use egui::{Color32, Stroke};
|
|
||||||
pub use flowchart::FlowChart;
|
|
||||||
|
|
||||||
const ITERATIONS: usize = 1_000;
|
|
||||||
const RESOLUTION: f32 = 0.6;
|
|
||||||
|
|
||||||
const TARGET_LINE_GAP: f32 = 80.;
|
|
||||||
|
|
||||||
const BG_STROKE: Stroke = Stroke {
|
|
||||||
width: 0.3,
|
|
||||||
color: Color32::GRAY,
|
|
||||||
};
|
|
||||||
|
|
||||||
const CONNECTION_STROKE: Stroke = Stroke {
|
|
||||||
width: 3.,
|
|
||||||
color: Color32::WHITE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GROUP_BORDER_MARGIN: f32 = 20.;
|
|
||||||
|
|
||||||
static REPULSION_STRENGTH: f32 = 100000.0; // repulsion_strength
|
|
||||||
static ATTRACTION_STRENGTH: f32 = 0.01; // attraction_strength
|
|
||||||
static CENTER_ATTRACTION_STRENGTH: f32 = 0.01; // attraction_strength
|
|
||||||
static GROUP_ATTRACTION_STRENGTH: f32 = 0.01; // attraction_strength
|
|
||||||
static REST_LENGTH: f32 = 50.0; // rest_length
|
|
||||||
static DAMPING: f32 = 0.9; // damping
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
use egui::{Color32, TextEdit};
|
|
||||||
use unshell::config::{ConfigStructField, InterfaceData, InterfaceStruct, config_struct};
|
|
||||||
|
|
||||||
use crate::config::Config;
|
|
||||||
|
|
||||||
struct ConfigInterface(Config);
|
|
||||||
|
|
||||||
pub fn render(
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
interface_struct: &InterfaceStruct,
|
|
||||||
interface_data: &mut InterfaceData,
|
|
||||||
) {
|
|
||||||
match (interface_struct, interface_data) {
|
|
||||||
(InterfaceStruct::ConfigStruct(interface), InterfaceData::ConfigStruct(data)) => {
|
|
||||||
render_config_struct(ui, interface, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_config_struct(
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
interface: &config_struct::ConfigStructKeys,
|
|
||||||
data: &mut config_struct::ConfigStructValues,
|
|
||||||
) {
|
|
||||||
for (interface, data) in interface.iter().zip(data) {
|
|
||||||
match (interface, data) {
|
|
||||||
(ConfigStructField::Header(text), serde_json::Value::Null) => {
|
|
||||||
ui.heading(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
(ConfigStructField::Text(text), serde_json::Value::Null) => {
|
|
||||||
ui.label(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
ConfigStructField::String {
|
|
||||||
default: _,
|
|
||||||
max_length,
|
|
||||||
protected,
|
|
||||||
},
|
|
||||||
serde_json::Value::String(value),
|
|
||||||
) => {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
let mut widget = TextEdit::singleline(value);
|
|
||||||
|
|
||||||
if let Some(limit) = &max_length {
|
|
||||||
widget = widget.char_limit(*limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
let password = *protected && !ui.button("👁").is_pointer_button_down_on();
|
|
||||||
|
|
||||||
widget = widget.password(password);
|
|
||||||
|
|
||||||
// if protected
|
|
||||||
// ui.selectable_label(show_plaintext, "👁")
|
|
||||||
// .on_hover_text("Show/hide password")
|
|
||||||
// .clicked();
|
|
||||||
// {
|
|
||||||
// widget = widget.password(true);
|
|
||||||
// }
|
|
||||||
|
|
||||||
ui.add(widget);
|
|
||||||
|
|
||||||
if let Some(limit) = max_length {
|
|
||||||
ui.label(format!("{}/{}", value.len(), limit));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(
|
|
||||||
ConfigStructField::Integer { default, min, max },
|
|
||||||
serde_json::Value::Number(number),
|
|
||||||
) => todo!(),
|
|
||||||
|
|
||||||
(interface, data) => {
|
|
||||||
ui.colored_label(
|
|
||||||
Color32::RED,
|
|
||||||
&format!("Incorrect type and value! {interface:?} and {data:?}"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
mod interface;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
path::PathBuf,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell::{Result, config::TreeMessage};
|
|
||||||
|
|
||||||
use crate::auth::Auth;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct InterfaceWindow {
|
|
||||||
path: PathBuf,
|
|
||||||
// #[serde(skip)]
|
|
||||||
// data_bind: Bind<String, ModuleError>,
|
|
||||||
#[serde(skip)]
|
|
||||||
state: Arc<Mutex<InterfaceWindowState>>,
|
|
||||||
// #[serde(skip)]
|
|
||||||
// promise: Option<PromiseWrapper<Result<TreeMessage>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InterfaceWindowState {
|
|
||||||
is_request: bool,
|
|
||||||
is_error: bool,
|
|
||||||
branch: Option<TreeMessage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InterfaceWindow {
|
|
||||||
pub fn update(&mut self, auth: &mut Auth, ui: &mut egui::Ui) {
|
|
||||||
// let data_bind = Bind::<InterfaceWindowState, ModuleError>::new(false);
|
|
||||||
|
|
||||||
ui.heading("Interface");
|
|
||||||
ui.label(self.path.to_string_lossy());
|
|
||||||
|
|
||||||
{
|
|
||||||
if !self.path.eq("/") && ui.button("Go up").clicked() {
|
|
||||||
self.go_up();
|
|
||||||
self.state.lock().unwrap().branch = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
let (is_request, is_error, is_some) = (
|
|
||||||
state_lock.is_request,
|
|
||||||
state_lock.is_error,
|
|
||||||
state_lock.branch.is_some(),
|
|
||||||
);
|
|
||||||
|
|
||||||
drop(state_lock);
|
|
||||||
|
|
||||||
if is_request {
|
|
||||||
ui.spinner();
|
|
||||||
} else if is_error {
|
|
||||||
self.reset_path();
|
|
||||||
let mut state_lock = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
state_lock.is_error = false;
|
|
||||||
state_lock.is_request = false;
|
|
||||||
state_lock.branch = None;
|
|
||||||
|
|
||||||
drop(state_lock)
|
|
||||||
} else if !is_some {
|
|
||||||
{
|
|
||||||
let mut state_lock = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
state_lock.is_request = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state_clone = self.state.clone();
|
|
||||||
auth.get(
|
|
||||||
&format!("/api/interface{}", self.path.display()),
|
|
||||||
move |response: Result<TreeMessage>| {
|
|
||||||
let mut state_lock = state_clone.lock().unwrap();
|
|
||||||
|
|
||||||
match response {
|
|
||||||
Ok(item) => {
|
|
||||||
state_lock.branch = Some(item);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
crate::log(&format!("Got error {err:?}"));
|
|
||||||
state_lock.is_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state_lock.is_request = false;
|
|
||||||
|
|
||||||
drop(state_lock);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
let state_clone = self.state.clone();
|
|
||||||
|
|
||||||
let mut state_lock = state_clone.lock().unwrap();
|
|
||||||
|
|
||||||
let branch = (state_lock.branch).as_mut().unwrap();
|
|
||||||
|
|
||||||
let clear = match branch {
|
|
||||||
TreeMessage::InterfaceAndValue(interface_struct, interface_data) => {
|
|
||||||
interface::render(ui, interface_struct, interface_data);
|
|
||||||
|
|
||||||
if ui.button("Save").clicked() {
|
|
||||||
auth.post(
|
|
||||||
&format!("/api/interface{}", self.path.display()),
|
|
||||||
&TreeMessage::State(interface_data.clone()),
|
|
||||||
move |response: Result<TreeMessage>| {
|
|
||||||
// debug!("{response:?}");
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
TreeMessage::Folder(items) => {
|
|
||||||
let mut clear = false;
|
|
||||||
for item in items {
|
|
||||||
if ui.button(&format!("Item {}", item)).clicked() {
|
|
||||||
self.go_down(item.clone());
|
|
||||||
clear = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clear
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if clear {
|
|
||||||
state_lock.branch = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(state_lock)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_path(&mut self) {
|
|
||||||
self.path = PathBuf::from("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn go_up(&mut self) {
|
|
||||||
if let Some(parent) = self.path.parent() {
|
|
||||||
self.path = PathBuf::from(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn go_down(&mut self, path: String) {
|
|
||||||
self.path = self.path.join(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InterfaceWindow {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
path: "/".into(),
|
|
||||||
// promise: None,
|
|
||||||
state: Arc::new(Mutex::new(InterfaceWindowState::default())),
|
|
||||||
// data_bind: Bind::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InterfaceWindowState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
is_request: false,
|
|
||||||
branch: None,
|
|
||||||
is_error: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
#![warn(clippy::all, rust_2018_idioms)]
|
|
||||||
#![macro_use]
|
|
||||||
// #[allow(unused_extern_crates)]
|
|
||||||
// extern crate log;
|
|
||||||
|
|
||||||
pub mod app;
|
|
||||||
mod auth;
|
|
||||||
mod blobs;
|
|
||||||
mod config;
|
|
||||||
mod flowchart;
|
|
||||||
mod interface;
|
|
||||||
mod log_viewer;
|
|
||||||
mod payload_config;
|
|
||||||
|
|
||||||
use std::time::Duration;
|
|
||||||
const FORCE_REDRAW_DELAY: Duration = Duration::from_millis(300);
|
|
||||||
|
|
||||||
// mod JsFunc {
|
|
||||||
// use wasm_bindgen::JsValue;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
pub fn log(text: &str);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(module = "/assets/sw.js")]
|
|
||||||
extern "C" {
|
|
||||||
pub fn httpGet(url: &str, ok_callback: JsValue);
|
|
||||||
pub fn httpPost(url: &str, data: &str, ok_callback: JsValue);
|
|
||||||
pub fn httpGetAuth(url: &str, auth: String, ok_callback: JsValue);
|
|
||||||
pub fn httpPostAuth(url: &str, auth: String, data: &str, ok_callback: JsValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When compiling to web using trunk:
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[wasm_bindgen(start)]
|
|
||||||
pub async fn run() {
|
|
||||||
use wasm_bindgen::JsCast as _;
|
|
||||||
|
|
||||||
use app::TemplateApp;
|
|
||||||
|
|
||||||
// Redirect `log` message to `console.log` and friends:
|
|
||||||
eframe::WebLogger::init(log::LevelFilter::Debug).ok();
|
|
||||||
|
|
||||||
let web_options = eframe::WebOptions::default();
|
|
||||||
|
|
||||||
let document = web_sys::window()
|
|
||||||
.expect("No window")
|
|
||||||
.document()
|
|
||||||
.expect("No document");
|
|
||||||
|
|
||||||
let canvas = document
|
|
||||||
.get_element_by_id("the_canvas_id")
|
|
||||||
.expect("Failed to find the_canvas_id")
|
|
||||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
|
||||||
.expect("the_canvas_id was not a HtmlCanvasElement");
|
|
||||||
|
|
||||||
let start_result = eframe::WebRunner::new()
|
|
||||||
.start(
|
|
||||||
canvas,
|
|
||||||
web_options,
|
|
||||||
Box::new(|cc| Ok(Box::new(TemplateApp::new(cc)))),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Remove the loading text and spinner:
|
|
||||||
if let Some(loading_text) = document.get_element_by_id("loading_text") {
|
|
||||||
match start_result {
|
|
||||||
Ok(_) => {
|
|
||||||
loading_text.remove();
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
loading_text.set_inner_html(
|
|
||||||
"<p> The app has crashed. See the developer console for details. </p>",
|
|
||||||
);
|
|
||||||
panic!("Failed to start eframe: {e:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// }
|
|
||||||
// #[cfg(not(target_arch = "wasm32"))]
|
|
||||||
// mod JsFunc {
|
|
||||||
|
|
||||||
// pub fn httpGet(url: &str, callback: fn() -> {}) {}
|
|
||||||
// }
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
use chrono::DateTime;
|
|
||||||
use chrono::Utc;
|
|
||||||
use egui::Color32;
|
|
||||||
use egui::TextStyle;
|
|
||||||
use egui_extras::Column;
|
|
||||||
use egui_extras::TableBuilder;
|
|
||||||
|
|
||||||
use crate::auth::Auth;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct LogViewer {
|
|
||||||
enable_location: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
state: Arc<Mutex<LogState>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct LogState {
|
|
||||||
logs: Vec<Record>,
|
|
||||||
// trees: Option<Vec<String>>,
|
|
||||||
// tree_keys: Option<HashMap<String, String>>,
|
|
||||||
is_requesting: bool,
|
|
||||||
requested_data: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub enum LogLevel {
|
|
||||||
Debug,
|
|
||||||
Info,
|
|
||||||
Warn,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct Record {
|
|
||||||
log_level: LogLevel,
|
|
||||||
location: Option<String>,
|
|
||||||
time: SystemTime,
|
|
||||||
message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Record {
|
|
||||||
pub fn display_level(&self, ui: &mut egui::Ui) {
|
|
||||||
match self.log_level {
|
|
||||||
LogLevel::Debug => ui.colored_label(Color32::LIGHT_BLUE, "DBUG"),
|
|
||||||
LogLevel::Info => ui.colored_label(Color32::DARK_GREEN, "INFO"),
|
|
||||||
LogLevel::Warn => ui.colored_label(Color32::YELLOW, "WARN"),
|
|
||||||
LogLevel::Error => ui.colored_label(Color32::RED, "ERR!"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub fn display_location(&self, ui: &mut egui::Ui) {
|
|
||||||
if let Some(ref location) = self.location {
|
|
||||||
ui.label(location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn display_time(&self, ui: &mut egui::Ui) {
|
|
||||||
let date: DateTime<Utc> = self.time.into();
|
|
||||||
let date = date.to_rfc2822().to_string();
|
|
||||||
ui.label(date);
|
|
||||||
}
|
|
||||||
pub fn display_message(&self, ui: &mut egui::Ui) {
|
|
||||||
ui.strong(&self.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogViewer {
|
|
||||||
pub fn update(&mut self, auth: &mut Auth, ui: &mut egui::Ui) {
|
|
||||||
ui.heading("Log Viewer");
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Refresh").clicked() {
|
|
||||||
self.refresh_logs(auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.checkbox(&mut self.enable_location, "Enable Location");
|
|
||||||
});
|
|
||||||
|
|
||||||
// let logs = ;
|
|
||||||
|
|
||||||
let body_text_size = TextStyle::Body.resolve(ui.style()).size;
|
|
||||||
use egui_extras::{Size, StripBuilder};
|
|
||||||
StripBuilder::new(ui)
|
|
||||||
.size(Size::remainder().at_least(100.0)) // for the table
|
|
||||||
.size(Size::exact(body_text_size))
|
|
||||||
.vertical(|mut strip| {
|
|
||||||
strip.cell(|ui| {
|
|
||||||
egui::ScrollArea::both()
|
|
||||||
.stick_to_bottom(true)
|
|
||||||
.show(ui, |ui| {
|
|
||||||
let table = TableBuilder::new(ui)
|
|
||||||
.striped(true)
|
|
||||||
.resizable(true)
|
|
||||||
.stick_to_bottom(true)
|
|
||||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
|
||||||
.column(Column::auto())
|
|
||||||
.column(Column::auto())
|
|
||||||
.column(Column::auto())
|
|
||||||
.min_scrolled_height(0.0)
|
|
||||||
.sense(egui::Sense::click());
|
|
||||||
|
|
||||||
let table = if self.enable_location {
|
|
||||||
table.column(Column::auto())
|
|
||||||
} else {
|
|
||||||
table
|
|
||||||
};
|
|
||||||
|
|
||||||
table
|
|
||||||
.header(20., |mut header| {
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Time");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Level");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Message");
|
|
||||||
});
|
|
||||||
if self.enable_location {
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.strong("Location");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.body(|mut body| {
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
// let logs = state_lock.logs.as_ref();
|
|
||||||
|
|
||||||
for log in state_lock.logs.iter() {
|
|
||||||
// // let runtime = self.current_runtimes
|
|
||||||
|
|
||||||
body.row(18., |mut row| {
|
|
||||||
row.col(|ui| log.display_time(ui));
|
|
||||||
row.col(|ui| log.display_level(ui));
|
|
||||||
row.col(|ui| log.display_message(ui));
|
|
||||||
if self.enable_location {
|
|
||||||
row.col(|ui| log.display_location(ui));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
let state_lock = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
match (
|
|
||||||
state_lock.is_requesting,
|
|
||||||
state_lock.logs.len() == 0,
|
|
||||||
state_lock.requested_data,
|
|
||||||
) {
|
|
||||||
(true, _, _) => {
|
|
||||||
drop(state_lock);
|
|
||||||
ui.spinner();
|
|
||||||
}
|
|
||||||
(false, true, true) => {
|
|
||||||
drop(state_lock);
|
|
||||||
ui.label("There are no logs");
|
|
||||||
}
|
|
||||||
(false, true, false) => {
|
|
||||||
drop(state_lock);
|
|
||||||
self.refresh_logs(auth);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
drop(state_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_logs(&self, auth: &mut Auth) {
|
|
||||||
let state_clone = self.state.clone();
|
|
||||||
{
|
|
||||||
let mut state_lock = self.state.lock().unwrap();
|
|
||||||
state_lock.logs.clear();
|
|
||||||
state_lock.is_requesting = true;
|
|
||||||
}
|
|
||||||
auth.get(&format!("/api/log/{}", 0), move |e: Vec<String>| {
|
|
||||||
let mut state_lock = state_clone.lock().unwrap();
|
|
||||||
state_lock.logs.append(
|
|
||||||
&mut e
|
|
||||||
.iter()
|
|
||||||
.map(|log| serde_json::from_str(log).unwrap())
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
state_lock.is_requesting = false;
|
|
||||||
state_lock.requested_data = true;
|
|
||||||
|
|
||||||
// crate::log(&format!("{e:?}"));
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LogViewer {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
enable_location: false,
|
|
||||||
state: Arc::new(Mutex::new(LogState::default())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#![warn(clippy::all, rust_2018_idioms)]
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
|
||||||
|
|
||||||
// When compiling natively:
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
fn main() -> eframe::Result {
|
|
||||||
// pretty_env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
|
||||||
|
|
||||||
// let native_options = eframe::NativeOptions {
|
|
||||||
// viewport: egui::ViewportBuilder::default()
|
|
||||||
// .with_inner_size([400.0, 300.0])
|
|
||||||
// .with_min_inner_size([300.0, 220.0]),
|
|
||||||
// ..Default::default()
|
|
||||||
// };
|
|
||||||
// eframe::run_native(
|
|
||||||
// "eframe template",
|
|
||||||
// native_options,
|
|
||||||
// Box::new(|cc| Ok(Box::new(TemplateApp::new(cc)))),
|
|
||||||
// )
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// use crate::payload_config::structs::ConfigStructField;
|
|
||||||
|
|
||||||
mod structs;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct PayloadConfig {
|
|
||||||
config_struct: structs::Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
// struct ServerConfigState {
|
|
||||||
// // config: Vec<PayloadConfig>
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl PayloadConfig {
|
|
||||||
pub fn update(&mut self, ui: &mut egui::Ui) {
|
|
||||||
if ui.button("export").clicked() {
|
|
||||||
crate::log(&self.config_struct.export());
|
|
||||||
}
|
|
||||||
// ui.heading("Test");
|
|
||||||
self.config_struct.update(ui);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PayloadConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
config_struct: structs::default_configurable(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use egui::TextEdit;
|
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
enum ConfigStructField {
|
|
||||||
Header(String),
|
|
||||||
Text(String),
|
|
||||||
String {
|
|
||||||
default: Option<String>,
|
|
||||||
max_length: Option<usize>,
|
|
||||||
protected: bool,
|
|
||||||
},
|
|
||||||
Integer {
|
|
||||||
default: i32,
|
|
||||||
min: Option<i32>,
|
|
||||||
max: Option<i32>,
|
|
||||||
},
|
|
||||||
// Checkbox
|
|
||||||
// Dropdown
|
|
||||||
// Collapsing header
|
|
||||||
// Slider
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct Config {
|
|
||||||
config: Vec<(String, ConfigStructField)>,
|
|
||||||
state: HashMap<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
fn new(config: Vec<(String, ConfigStructField)>) -> Self {
|
|
||||||
Self {
|
|
||||||
config,
|
|
||||||
state: HashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, ui: &mut egui::Ui) {
|
|
||||||
for (id, field) in &self.config {
|
|
||||||
match field {
|
|
||||||
ConfigStructField::Header(text) => {
|
|
||||||
ui.heading(text);
|
|
||||||
}
|
|
||||||
ConfigStructField::Text(text) => {
|
|
||||||
ui.label(text);
|
|
||||||
}
|
|
||||||
ConfigStructField::String {
|
|
||||||
default,
|
|
||||||
max_length,
|
|
||||||
protected,
|
|
||||||
} => {
|
|
||||||
let value = if let Some(Value::String(value)) = self.state.get_mut(id) {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
self.state.insert(
|
|
||||||
id.clone(),
|
|
||||||
Value::String(default.clone().unwrap_or(String::new())),
|
|
||||||
);
|
|
||||||
if let Some(Value::String(value)) = self.state.get_mut(id) {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut widget = TextEdit::singleline(value).password(*protected);
|
|
||||||
|
|
||||||
if let Some(limit) = &max_length {
|
|
||||||
widget = widget.char_limit(*limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.add(widget);
|
|
||||||
}
|
|
||||||
_ => {} // ConfigStructField::Integer { default, min, max } => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// match &self.field {
|
|
||||||
// ConfigStructField::Header(text) => {
|
|
||||||
// ui.heading(text);
|
|
||||||
// }
|
|
||||||
// ConfigStructField::Text(text) => {
|
|
||||||
// ui.label(text);
|
|
||||||
// }
|
|
||||||
// ConfigStructField::String {
|
|
||||||
// default,
|
|
||||||
// max_length,
|
|
||||||
// protected,
|
|
||||||
// } => ui.text_edit_singleline(),
|
|
||||||
// ConfigStructField::Integer { default, min, max } => todo!(),
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn export(&self) -> String {
|
|
||||||
serde_json::to_string(&self.config).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_configurable() -> Config {
|
|
||||||
Config::new(vec![
|
|
||||||
(
|
|
||||||
"Header".into(),
|
|
||||||
ConfigStructField::Header("Test Header!".into()),
|
|
||||||
),
|
|
||||||
("text".into(), ConfigStructField::Text("Test Text!".into())),
|
|
||||||
(
|
|
||||||
"Config".into(),
|
|
||||||
ConfigStructField::String {
|
|
||||||
default: Some("Test String".into()),
|
|
||||||
max_length: Some(30),
|
|
||||||
protected: false,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"Protected".into(),
|
|
||||||
ConfigStructField::String {
|
|
||||||
default: Some("Protected String".into()),
|
|
||||||
max_length: None,
|
|
||||||
protected: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
Generated
-845
@@ -1,845 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cipher",
|
|
||||||
"cpufeatures",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android_system_properties"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
|
||||||
dependencies = [
|
|
||||||
"bincode_derive",
|
|
||||||
"serde",
|
|
||||||
"unty",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode_derive"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
|
|
||||||
dependencies = [
|
|
||||||
"virtue",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b"
|
|
||||||
dependencies = [
|
|
||||||
"hybrid-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.19.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cbc"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
|
||||||
dependencies = [
|
|
||||||
"cipher",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.2.50"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
|
|
||||||
dependencies = [
|
|
||||||
"find-msvc-tools",
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
|
||||||
dependencies = [
|
|
||||||
"iana-time-zone",
|
|
||||||
"js-sys",
|
|
||||||
"num-traits",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cipher"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|
||||||
dependencies = [
|
|
||||||
"crypto-common",
|
|
||||||
"inout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation-sys"
|
|
||||||
version = "0.8.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.2.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-common"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.10.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer",
|
|
||||||
"crypto-common",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "find-msvc-tools"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-array"
|
|
||||||
version = "0.14.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"r-efi",
|
|
||||||
"wasip2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex-literal"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hybrid-array"
|
|
||||||
version = "0.4.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone"
|
|
||||||
version = "0.1.64"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
|
||||||
dependencies = [
|
|
||||||
"android_system_properties",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"iana-time-zone-haiku",
|
|
||||||
"js-sys",
|
|
||||||
"log",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone-haiku"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inout"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
|
||||||
dependencies = [
|
|
||||||
"block-padding 0.3.3",
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.83"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.178"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libloading"
|
|
||||||
version = "0.8.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
|
||||||
dependencies = [
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot"
|
|
||||||
version = "0.12.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot_core"
|
|
||||||
version = "0.9.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"smallvec",
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.103"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
|
||||||
dependencies = [
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.5.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.12.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
||||||
dependencies = [
|
|
||||||
"serde_core",
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_core"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_json"
|
|
||||||
version = "1.0.145"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"memchr",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha2"
|
|
||||||
version = "0.10.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_init"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"cfg_aliases 0.2.1",
|
|
||||||
"libc",
|
|
||||||
"parking_lot",
|
|
||||||
"parking_lot_core",
|
|
||||||
"static_init_macro",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_init_macro"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f"
|
|
||||||
dependencies = [
|
|
||||||
"cfg_aliases 0.1.1",
|
|
||||||
"memchr",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.111"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-crypt"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"aes",
|
|
||||||
"block-padding 0.4.2",
|
|
||||||
"cbc",
|
|
||||||
"getrandom",
|
|
||||||
"hex",
|
|
||||||
"hex-literal",
|
|
||||||
"regex",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-lib"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-manager"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bincode",
|
|
||||||
"libc",
|
|
||||||
"libloading",
|
|
||||||
"rand",
|
|
||||||
"unshell-lib",
|
|
||||||
"unshell-obfuscate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unshell-obfuscate"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rand",
|
|
||||||
"static_init",
|
|
||||||
"syn 2.0.111",
|
|
||||||
"unshell-crypt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unty"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "virtue"
|
|
||||||
version = "0.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasip2"
|
|
||||||
version = "1.0.1+wasi-0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"rustversion",
|
|
||||||
"wasm-bindgen-macro",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.62.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement",
|
|
||||||
"windows-interface",
|
|
||||||
"windows-link",
|
|
||||||
"windows-result",
|
|
||||||
"windows-strings",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.60.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.59.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-link"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wit-bindgen"
|
|
||||||
version = "0.46.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.111",
|
|
||||||
]
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ush-manager"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
include.workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
log = ["unshell/log"]
|
|
||||||
log_debug = ["unshell/log_debug"]
|
|
||||||
|
|
||||||
obfuscate = ["unshell/obfuscate"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
unshell = {path = "../", default-features = false}
|
|
||||||
|
|
||||||
bincode = "2.0.1"
|
|
||||||
libc = "0.2.178"
|
|
||||||
libloading = "0.8.9"
|
|
||||||
rand = "0.9.2"
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
// use std::collections::HashMap;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use unshell::Result;
|
|
||||||
use unshell::config::RuntimeConfig;
|
|
||||||
|
|
||||||
use crate::ModuleRuntime;
|
|
||||||
|
|
||||||
pub struct PayloadConfig {
|
|
||||||
pub id: &'static str,
|
|
||||||
pub components: Vec<NamedComponent>,
|
|
||||||
pub runtime_config: Vec<RuntimeConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NamedComponent {
|
|
||||||
pub name: &'static str,
|
|
||||||
|
|
||||||
// + Sync + Sync + Sync + Sync + Sync + Sync + Sync + Sync
|
|
||||||
pub get_interface: &'static (dyn Fn() -> Option<&'static (dyn InterfaceWrapper + Sync)> + Sync),
|
|
||||||
pub start_runtime:
|
|
||||||
&'static (dyn Fn(&'static RuntimeConfig) -> Result<Box<dyn ModuleRuntime>> + Sync),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for NamedComponent {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("NamedComponent")
|
|
||||||
.field("name", &self.name)
|
|
||||||
// .field("get_interface", &self.get_interface)
|
|
||||||
// .field("start_runtime", &self.start_runtime)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait that wraps the get_interface<T>() function inside of components
|
|
||||||
pub trait InterfaceWrapper: Send + Sync {
|
|
||||||
fn get_interface<T: 'static>(&self) -> Option<T>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
mod manager;
|
|
||||||
mod module;
|
|
||||||
mod module_interface;
|
|
||||||
// pub mod network;
|
|
||||||
mod proc_load;
|
|
||||||
|
|
||||||
pub mod interface;
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
pub use manager::Manager;
|
|
||||||
pub use module::Module;
|
|
||||||
|
|
||||||
pub use interface::{InterfaceWrapper, NamedComponent, PayloadConfig};
|
|
||||||
|
|
||||||
// extern crate unshell_lib;
|
|
||||||
use unshell::Result;
|
|
||||||
|
|
||||||
/// Trait for defining modules that have a runtime.
|
|
||||||
pub trait ModuleRuntime: Send + Sync {
|
|
||||||
fn init(&mut self, manager: Arc<Mutex<Manager>>) -> Result<()>;
|
|
||||||
|
|
||||||
/// Returns true if the module is running.
|
|
||||||
/// After returning false, the module will be dropped.
|
|
||||||
fn is_running(&self) -> bool;
|
|
||||||
/// Consumes the module, implementation should kill whatever is running.
|
|
||||||
fn kill(self: Box<Self>);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
use crate::Manager;
|
|
||||||
use unshell::Announcement;
|
|
||||||
|
|
||||||
impl Manager {
|
|
||||||
pub fn recv_announcement(&mut self, announcement: &Announcement) {
|
|
||||||
match announcement {
|
|
||||||
Announcement::TestAnnouncement(str) => {
|
|
||||||
println!("Got test announcement: {}", str)
|
|
||||||
} // Announcement::GetRuntimes => todo!(),
|
|
||||||
// Announcement::GetRuntimesAck(_) => todo!(),
|
|
||||||
// Announcement::StartRuntime(runtime_config) => todo!(),
|
|
||||||
// Announcement::StartRuntimeAck(_) => todo!(),
|
|
||||||
// _ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use unshell_lib::{Announcement, Result};
|
|
||||||
|
|
||||||
// use crate::network::Stream;
|
|
||||||
|
|
||||||
use crate::Manager;
|
|
||||||
|
|
||||||
impl Manager {
|
|
||||||
pub fn add_connection(&mut self, connection: Box<dyn Stream<Announcement>>) {
|
|
||||||
self.connections.push(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prune_connections(&mut self) {
|
|
||||||
self.connections.retain(|c| c.is_alive());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recv_connection_announcements(&mut self) {
|
|
||||||
// Collect all incoming announcements
|
|
||||||
let announcements = self
|
|
||||||
.connections
|
|
||||||
.iter_mut()
|
|
||||||
.map(|c| c.try_read())
|
|
||||||
.flat_map(|array| array)
|
|
||||||
.collect::<Vec<Announcement>>();
|
|
||||||
|
|
||||||
for announcement in announcements {
|
|
||||||
self.recv_announcement(&announcement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn broadcast(&mut self, announcement: Announcement) -> Result<()> {
|
|
||||||
// for connection in &mut self.connections {
|
|
||||||
// connection.write(announcement.clone())?;
|
|
||||||
// }
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
mod announcement;
|
|
||||||
// mod connection;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
thread::{self, JoinHandle},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell::obfuscate::symbol;
|
|
||||||
use unshell::{Result, config::RuntimeConfig, debug, warn};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
ModuleRuntime,
|
|
||||||
interface::{NamedComponent, PayloadConfig},
|
|
||||||
module::Module,
|
|
||||||
// network::Stream,
|
|
||||||
};
|
|
||||||
|
|
||||||
// #[derive(Debug)]
|
|
||||||
pub struct Manager {
|
|
||||||
id: &'static str,
|
|
||||||
|
|
||||||
handle: Option<JoinHandle<()>>,
|
|
||||||
|
|
||||||
pub modules: Vec<Module>,
|
|
||||||
|
|
||||||
components: HashMap<String, NamedComponent>,
|
|
||||||
active_runtimes: Vec<Box<dyn ModuleRuntime>>,
|
|
||||||
// pub connections: Vec<Box<dyn Stream<Announcement>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// static mut MANAGER_RUNTIME: Option<Arc<Mutex<Manager>>> = None;
|
|
||||||
|
|
||||||
impl Manager {
|
|
||||||
fn new(id: &'static str, components: Vec<NamedComponent>, modules: Vec<Module>) -> Self {
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
handle: None,
|
|
||||||
|
|
||||||
modules,
|
|
||||||
components: components
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| (c.name.to_string(), c))
|
|
||||||
.collect(),
|
|
||||||
active_runtimes: Vec::new(),
|
|
||||||
// connections: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create Manager, and run initialization for each Module
|
|
||||||
#[allow(static_mut_refs)]
|
|
||||||
pub fn start(config: &'static PayloadConfig, modules: Vec<Module>) -> Arc<Mutex<Self>> {
|
|
||||||
// Construct self
|
|
||||||
let mut this = Self::new(&config.id, config.components.clone(), modules);
|
|
||||||
|
|
||||||
debug!("Imported {} base components", this.components.len());
|
|
||||||
debug!("Imported {} base runtimes", &config.runtime_config.len());
|
|
||||||
|
|
||||||
// Load each of the pre-prepared modules
|
|
||||||
this.load_components();
|
|
||||||
|
|
||||||
let this = Arc::new(Mutex::new(this));
|
|
||||||
|
|
||||||
debug!("Creating runtimes...");
|
|
||||||
for runtime in &config.runtime_config {
|
|
||||||
Self::create_runtime(this.clone(), runtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Starting runtimes...");
|
|
||||||
for runtime in &mut this.lock().unwrap().active_runtimes {
|
|
||||||
if let Err(e) = runtime.init(this.clone()) {
|
|
||||||
warn!("Failed to start runtime: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lock().unwrap().handle = Some(Self::start_thread(this.clone()));
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_components(&mut self) {
|
|
||||||
for module in &self.modules {
|
|
||||||
// Load get_components function from shared object library
|
|
||||||
let component_func = match module
|
|
||||||
.get_symbol::<fn() -> Vec<NamedComponent>>(symbol!("get_components").as_bytes())
|
|
||||||
{
|
|
||||||
Ok(func) => func,
|
|
||||||
Err(_) => {
|
|
||||||
warn!("get_components function not found");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let components = component_func();
|
|
||||||
let component_name = "TODO"; //TODO: Make this actually load component name
|
|
||||||
|
|
||||||
debug!("{} - Retrieved payload metadata", component_name);
|
|
||||||
|
|
||||||
// Add each component into self
|
|
||||||
for c in components {
|
|
||||||
debug!("{} - Found component '{}'", "TODO", c.name);
|
|
||||||
self.components.insert(c.name.to_owned(), c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The manager thread. receives announcements, and kills runtimes.
|
|
||||||
fn start_thread(this: Arc<Mutex<Self>>) -> JoinHandle<()> {
|
|
||||||
thread::spawn(move || {
|
|
||||||
loop {
|
|
||||||
thread::sleep(Duration::from_millis(10));
|
|
||||||
|
|
||||||
let mut this_lock = this.lock().unwrap();
|
|
||||||
|
|
||||||
if this_lock.active_runtimes.len() <= 0 {
|
|
||||||
debug!("There are no more runtimes! Exiting...");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this_lock.active_runtimes.retain(|runtime| {
|
|
||||||
if runtime.is_running() {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
debug!("Runtime exited!"); //TODO: Make this better
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// // Read announcements
|
|
||||||
// this_lock.recv_connection_announcements();
|
|
||||||
|
|
||||||
// // Prune dead connections
|
|
||||||
// this_lock.prune_connections();
|
|
||||||
|
|
||||||
drop(this_lock)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wait for manager thread to finish.
|
|
||||||
pub fn join(this: Arc<Mutex<Self>>) {
|
|
||||||
loop {
|
|
||||||
if this.lock().unwrap().handle.as_ref().unwrap().is_finished() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start a runtime
|
|
||||||
fn create_runtime<'a>(this: Arc<Mutex<Self>>, runtime: &'static RuntimeConfig) {
|
|
||||||
let mut this_lock = this.lock().unwrap();
|
|
||||||
|
|
||||||
let component = match this_lock.components.get(&runtime.parent_component) {
|
|
||||||
Some(component) => component,
|
|
||||||
None => {
|
|
||||||
warn!(
|
|
||||||
"Could not find component '{}' which is referenced by runtime: {}",
|
|
||||||
runtime.parent_component, runtime.name
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("Starting runtime: {}", runtime.name);
|
|
||||||
|
|
||||||
let runtime = match (*component.start_runtime)(runtime) {
|
|
||||||
Ok(runtime) => runtime,
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Failed to start runtime: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this_lock.active_runtimes.push(runtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_runtime(this: Arc<Mutex<Self>>, runtime: &'static RuntimeConfig) -> Result<()> {
|
|
||||||
Self::create_runtime(this.clone(), runtime);
|
|
||||||
|
|
||||||
this.lock()
|
|
||||||
.unwrap()
|
|
||||||
.active_runtimes
|
|
||||||
.iter_mut()
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.init(this.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_name(&self) -> &str {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
use libloading::{Library, Symbol};
|
|
||||||
use unshell::{
|
|
||||||
ModuleError, Result,
|
|
||||||
logger::{self, SetupLogger, logger},
|
|
||||||
warn,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::proc_load::memfd_create_dlopen;
|
|
||||||
|
|
||||||
pub struct Module {
|
|
||||||
lib: Library,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
pub fn new(path: &str) -> Result<Self> {
|
|
||||||
let lib = unsafe { Library::new(&path) }
|
|
||||||
.map_err(|e| ModuleError::LibLoadingError(e.to_string()))?;
|
|
||||||
|
|
||||||
let this = Self { lib };
|
|
||||||
|
|
||||||
if let Ok(setup_logger) = this.get_symbol::<SetupLogger>(b"setup_logger") {
|
|
||||||
setup_logger(logger::logger());
|
|
||||||
} else {
|
|
||||||
warn!("setup_logger not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement actual reflective ELF loading (possibly even custom format)
|
|
||||||
// Look at https://github.com/weizhiao/rust-elfloader
|
|
||||||
pub fn new_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
let lib =
|
|
||||||
memfd_create_dlopen(bytes).map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
|
||||||
|
|
||||||
let this = Self { lib };
|
|
||||||
|
|
||||||
if let Ok(setup_logger) = this.get_symbol::<SetupLogger>(b"setup_logger") {
|
|
||||||
setup_logger(logger());
|
|
||||||
} else {
|
|
||||||
warn!("setup_logger not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(this)
|
|
||||||
}
|
|
||||||
pub fn get_symbol<T>(&self, symbol: &[u8]) -> Result<Symbol<'_, T>> {
|
|
||||||
let symbol = unsafe { self.lib.get::<T>(symbol) }
|
|
||||||
.map_err(|e| ModuleError::LinkError(format!("Failed to load symbol: {}", e)))?;
|
|
||||||
|
|
||||||
Ok(symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/// "Module Interface" helper macro that creates a struct with function pointers
|
|
||||||
/// Useful for defining and requiring modules' functions accross FFI boundry.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! module_interface {
|
|
||||||
($(#[$struct_meta:meta])* $interface_name:ident { $($(#[$fn_meta:meta])* fn $fn_name:ident $(<$($gen:ident),+ $(,)?>)?($($arg:ident : $ty:ty),* $(,)?) $(-> $ret:ty)? $(where $($where_clause:tt)*)?);* $(;)? }) => {
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[allow(improper_ctypes_definitions)]
|
|
||||||
$(#[$struct_meta])*
|
|
||||||
pub struct $interface_name {
|
|
||||||
$(
|
|
||||||
// This line will FAIL TO COMPILE if you use generics in the macro input.
|
|
||||||
// You MUST use concrete types like *mut c_void for "generic" data.
|
|
||||||
$fn_name: extern "C" fn($($ty),*) $(-> $ret)?,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $interface_name {
|
|
||||||
$(
|
|
||||||
#[inline(always)]
|
|
||||||
$(#[$fn_meta])* // Propagate function attributes
|
|
||||||
// This is the fix for the `impl` block.
|
|
||||||
// It adds the captured generics and where-clause to the wrapper function.
|
|
||||||
pub fn $fn_name $(<$($gen),+>)? (&self, $($arg: $ty),*) $(-> $ret)?
|
|
||||||
$(where $($where_clause)*)?
|
|
||||||
{
|
|
||||||
(self.$fn_name)($($arg),*)
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
|
|
||||||
/// Create from raw function pointers
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure all function pointers are valid and have
|
|
||||||
/// the correct signatures
|
|
||||||
pub fn from_raw(
|
|
||||||
$($fn_name: extern "C" fn($($ty),*) $(-> $ret)?),*
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
$($fn_name),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl crate::module::Interface for $interface_name {
|
|
||||||
// fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
|
||||||
// self
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// mod connection;
|
|
||||||
mod tcp_stream;
|
|
||||||
|
|
||||||
pub use tcp_stream::TcpStream;
|
|
||||||
|
|
||||||
use unshell_lib::ModuleError;
|
|
||||||
|
|
||||||
/// This is the data transmission type
|
|
||||||
pub trait Stream<T>: Send + Sync {
|
|
||||||
// fn get_info(&self) -> String;
|
|
||||||
fn is_alive(&self) -> bool;
|
|
||||||
|
|
||||||
fn has_recv(&self) -> bool;
|
|
||||||
|
|
||||||
/// Possibly blocking stream read function
|
|
||||||
fn read(&mut self) -> Vec<T>;
|
|
||||||
|
|
||||||
/// Non-blocking read function
|
|
||||||
fn try_read(&mut self) -> Vec<T> {
|
|
||||||
if self.has_recv() {
|
|
||||||
self.read()
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, data: T) -> Result<(), ModuleError>;
|
|
||||||
|
|
||||||
fn try_clone(&self) -> Result<Box<dyn Stream<T> + Send + Sync>, ModuleError>;
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
use std::{
|
|
||||||
io::{Read, Write},
|
|
||||||
net,
|
|
||||||
sync::{
|
|
||||||
Arc,
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell_lib::{Announcement, ModuleError, Result, debug};
|
|
||||||
|
|
||||||
use crate::network::Stream;
|
|
||||||
|
|
||||||
pub struct TcpStream(Arc<AtomicBool>, net::TcpStream);
|
|
||||||
|
|
||||||
impl TcpStream {
|
|
||||||
pub fn new(stream: net::TcpStream) -> Self {
|
|
||||||
stream.set_nonblocking(true).unwrap();
|
|
||||||
Self(Arc::new(AtomicBool::new(true)), stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call this when the stream ends
|
|
||||||
fn disconnected(&mut self) {
|
|
||||||
self.0.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stream<Announcement> for TcpStream {
|
|
||||||
fn is_alive(&self) -> bool {
|
|
||||||
// if self.1.take_error().unwrap_or(None).is_some() {
|
|
||||||
// // self.1.pe
|
|
||||||
// warn!("Disconnected #################");
|
|
||||||
// return true;
|
|
||||||
// } else {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut buf = [0u8; 1];
|
|
||||||
// match self.1.peek(&mut buf) {
|
|
||||||
// Ok(n) => n == 1,
|
|
||||||
// Err(_) => false,
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut buf = [0u8; 1];
|
|
||||||
match self.1.peek(&mut buf) {
|
|
||||||
Ok(0) => false, // Connection closed (EOF)
|
|
||||||
Ok(_) => true, // Data available or connection alive
|
|
||||||
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => true, // No data but alive
|
|
||||||
Err(_) => false, // Connection error
|
|
||||||
}
|
|
||||||
|
|
||||||
// true
|
|
||||||
|
|
||||||
// self.0.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_recv(&self) -> bool {
|
|
||||||
let mut buf = [0u8; 1];
|
|
||||||
match self.1.peek(&mut buf) {
|
|
||||||
Ok(n) if n > 0 => true, // Data is available
|
|
||||||
Ok(_) => false, // EOF (connection closed)
|
|
||||||
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => false, // No data
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
// false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&mut self) -> Vec<Announcement> {
|
|
||||||
let mut ret = Vec::new();
|
|
||||||
|
|
||||||
while self.has_recv() {
|
|
||||||
let mut size_buf = [0u8; 4];
|
|
||||||
match self.1.read_exact(&mut size_buf) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(_) => {
|
|
||||||
self.disconnected();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let size = u32::from_be_bytes(size_buf);
|
|
||||||
|
|
||||||
let mut buf = vec![0u8; size as usize];
|
|
||||||
|
|
||||||
match self.1.read_exact(&mut buf) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(_) => {
|
|
||||||
self.disconnected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(a) = Announcement::decode(&buf) {
|
|
||||||
ret.push(a);
|
|
||||||
} else {
|
|
||||||
debug!("Malformed data");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, announcement: Announcement) -> Result<()> {
|
|
||||||
let bytes = announcement.encode();
|
|
||||||
|
|
||||||
// Write length of bytes
|
|
||||||
self.1
|
|
||||||
.write_all(&u32::to_be_bytes(bytes.len() as u32))
|
|
||||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
|
||||||
// Write data
|
|
||||||
self.1
|
|
||||||
.write_all(&bytes)
|
|
||||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
|
||||||
// Flush data
|
|
||||||
self.1
|
|
||||||
.flush()
|
|
||||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_clone(&self) -> Result<Box<dyn Stream<Announcement> + Send + Sync>> {
|
|
||||||
Ok(Box::new(Self(
|
|
||||||
self.0.clone(),
|
|
||||||
self.1
|
|
||||||
.try_clone()
|
|
||||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
// Load a shared object by saving bytes to a filesystem in /proc
|
|
||||||
|
|
||||||
use std::{ffi::CString, io};
|
|
||||||
|
|
||||||
use libloading::Library;
|
|
||||||
use unshell::{ModuleError, Result, warn};
|
|
||||||
|
|
||||||
// The `memfd_create` syscall flags (MFD_CLOEXEC is common and good practice)
|
|
||||||
const MFD_CLOEXEC: u32 = 0x0001;
|
|
||||||
const MFD_ALLOW_SEALING: u32 = 0x0002;
|
|
||||||
|
|
||||||
pub fn memfd_create_dlopen(payload: &[u8]) -> Result<Library> {
|
|
||||||
use rand::distr::{Alphanumeric, SampleString};
|
|
||||||
|
|
||||||
let string = Alphanumeric.sample_string(&mut rand::rng(), 16);
|
|
||||||
|
|
||||||
// 1. Create the anonymous in-memory file descriptor using the raw syscall
|
|
||||||
let c_name = CString::new(string)
|
|
||||||
.map_err(|e| ModuleError::LibLoadingError(format!("CString conversion failed: {e:?}")))?;
|
|
||||||
|
|
||||||
let fd = unsafe { libc::memfd_create(c_name.as_ptr(), MFD_CLOEXEC | MFD_ALLOW_SEALING) };
|
|
||||||
|
|
||||||
if fd < 0 {
|
|
||||||
return Err(ModuleError::LibLoadingError(format!(
|
|
||||||
"IO Error {:?}",
|
|
||||||
io::Error::last_os_error().to_string()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Write the payload bytes to the in-memory file
|
|
||||||
let bytes_written =
|
|
||||||
unsafe { libc::write(fd, payload.as_ptr() as *const libc::c_void, payload.len()) };
|
|
||||||
|
|
||||||
if bytes_written != payload.len() as isize {
|
|
||||||
// If write fails or is incomplete, clean up the file descriptor
|
|
||||||
unsafe {
|
|
||||||
libc::close(fd);
|
|
||||||
}
|
|
||||||
return Err(ModuleError::LibLoadingError(
|
|
||||||
"Failed to write full payload to memfd".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Seal the file to prevent modification, common for security/integrity
|
|
||||||
// Note: The MFD_ALLOW_SEALING flag must be set during creation for this to work.
|
|
||||||
let seals = libc::F_SEAL_GROW | libc::F_SEAL_SHRINK | libc::F_SEAL_WRITE;
|
|
||||||
if unsafe { libc::fcntl(fd, libc::F_ADD_SEALS, seals) } == -1 {
|
|
||||||
// Log a warning but continue if sealing fails (e.g., due to permissions)
|
|
||||||
warn!(
|
|
||||||
"memfd_create_dlopen: Failed to apply seals. Error: {}",
|
|
||||||
io::Error::last_os_error()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Construct the virtual path to the in-memory file
|
|
||||||
// This path is necessary for dlopen to work, as dlopen expects a filesystem path.
|
|
||||||
let dl_path = format!("/proc/self/fd/{}", fd);
|
|
||||||
|
|
||||||
// 4. Use dlopen (via libloading) on the virtual path
|
|
||||||
Ok(unsafe {
|
|
||||||
Library::new(&dl_path)
|
|
||||||
.map_err(|e| ModuleError::LibLoadingError(format!("Failed to import library: {}", e)))?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -6,22 +6,24 @@ authors.workspace = true
|
|||||||
include.workspace = true
|
include.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
obfuscate = []
|
default = []
|
||||||
|
obfuscate_aes = []
|
||||||
|
obfuscate_ref = []
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes = "0.8.4"
|
base62 = {path = "../base62"}
|
||||||
|
|
||||||
|
# aes = "0.8.4"
|
||||||
block-padding = "0.4.1"
|
block-padding = "0.4.1"
|
||||||
cbc = "0.1.2"
|
# cbc = "0.1.2"
|
||||||
getrandom = "0.3.4"
|
getrandom = "0.3.4"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
hex-literal = "1.1.0"
|
hex-literal = "1.1.0"
|
||||||
regex = "1.12.2"
|
# regex = "1.12.2"
|
||||||
sha2 = "0.10.9"
|
# sha2 = "0.10.9"
|
||||||
|
|
||||||
# unshell-crypt = {path = "../unshell-crypt"}
|
|
||||||
|
|
||||||
# Common
|
# Common
|
||||||
static_init = { workspace = true }
|
static_init = { workspace = true }
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
use crate::crypt::{base62::Base62, hash};
|
|
||||||
use aes::cipher::{BlockEncryptMut, KeyIvInit};
|
|
||||||
use cbc::cipher::block_padding::Pkcs7;
|
|
||||||
|
|
||||||
fn pkcs7_padded_length(input_len: usize) -> usize {
|
|
||||||
let block_size = 16;
|
|
||||||
((input_len / block_size) + 1) * block_size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encrypt_aes(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
|
|
||||||
let plaintext = plaintext.as_bytes();
|
|
||||||
|
|
||||||
// Hash the env key to get a 32-byte (256-bit) AES key
|
|
||||||
let key = hash(key_str.as_bytes());
|
|
||||||
|
|
||||||
// Generate a psudo-random salt byte based on the plaintext
|
|
||||||
// I hope this does not break the encryption.
|
|
||||||
let mut salt = 0;
|
|
||||||
|
|
||||||
for byte in plaintext {
|
|
||||||
salt ^= byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut key_salted = key.clone();
|
|
||||||
|
|
||||||
// Salt the key by XORing the salt byte with all the key bytes.
|
|
||||||
// This ensures that the "hash" generated from the plaintext will
|
|
||||||
// make the encrypted result extremely different.
|
|
||||||
for i in 0..32 {
|
|
||||||
key_salted[i] ^= salt;
|
|
||||||
}
|
|
||||||
|
|
||||||
let buf_len = pkcs7_padded_length(plaintext.len());
|
|
||||||
|
|
||||||
let mut buf = vec![0u8; buf_len];
|
|
||||||
let pt_len = plaintext.len();
|
|
||||||
buf[..pt_len].copy_from_slice(&plaintext);
|
|
||||||
|
|
||||||
let mut ct = cbc::Encryptor::<aes::Aes256>::new(&key_salted.into(), &iv.into())
|
|
||||||
.encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
|
|
||||||
.unwrap()
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
// Add the salt byte to the key byte,
|
|
||||||
ct.insert(0, salt);
|
|
||||||
|
|
||||||
// Encode result in base62
|
|
||||||
Base62::encode_full(&ct, &key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encrypt_aes_lines(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
|
|
||||||
format!("_{}_", encrypt_aes(plaintext, key_str, iv))
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const ENV_KEY_NAME: &str = "OBFUSCATION_KEY";
|
||||||
|
const BACKUP_ENV_KEY: &str = "OBFUSCATION_KEY_DO_NOT_USE";
|
||||||
|
|
||||||
|
pub fn get_encryption_key() -> String {
|
||||||
|
if let Ok(key) = std::env::var(ENV_KEY_NAME) {
|
||||||
|
key
|
||||||
|
} else {
|
||||||
|
println!("Using default encryption key!");
|
||||||
|
BACKUP_ENV_KEY.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,72 @@
|
|||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::{Expr, Lit, Token};
|
use syn::{Expr, Lit, Token, parse_macro_input};
|
||||||
|
|
||||||
pub struct PrintlnArgs {
|
pub fn sym_format(input: TokenStream) -> TokenStream {
|
||||||
pub format_str: String,
|
let PrintlnArgs { format_str, args } = parse_macro_input!(input as PrintlnArgs);
|
||||||
pub args: Vec<Expr>,
|
|
||||||
|
let segments = parse_format_string(&format_str);
|
||||||
|
|
||||||
|
if segments.is_empty() {
|
||||||
|
return quote! {
|
||||||
|
print!("\n")
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
|
||||||
|
for segment in segments {
|
||||||
|
match segment {
|
||||||
|
FormatSegment::Static(text) => {
|
||||||
|
parts.push(quote! {
|
||||||
|
obfuscate::sym!(#text).to_string()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FormatSegment::Dynamic(spec, idx) => {
|
||||||
|
if idx >= args.len() {
|
||||||
|
return syn::Error::new(
|
||||||
|
proc_macro2::Span::call_site(),
|
||||||
|
format!("argument {} is missing", idx),
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg = &args[idx];
|
||||||
|
let fmt_spec = if spec.is_empty() {
|
||||||
|
quote! { "{}" }
|
||||||
|
} else {
|
||||||
|
let full_spec = format!("{{{}}}", spec);
|
||||||
|
quote! { #full_spec }
|
||||||
|
};
|
||||||
|
|
||||||
|
// quote! {
|
||||||
|
// println!(#fmt_spec, #arg);
|
||||||
|
// }
|
||||||
|
parts.push(quote! {
|
||||||
|
format!(#fmt_spec, #arg)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
{
|
||||||
|
let mut string = String::new();
|
||||||
|
#(
|
||||||
|
string.push_str(&#parts);
|
||||||
|
)*
|
||||||
|
string
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrintlnArgs {
|
||||||
|
format_str: String,
|
||||||
|
args: Vec<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for PrintlnArgs {
|
impl Parse for PrintlnArgs {
|
||||||
@@ -40,12 +103,12 @@ impl Parse for PrintlnArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FormatSegment {
|
enum FormatSegment {
|
||||||
Static(String),
|
Static(String),
|
||||||
Dynamic(String, usize), // format spec, arg index
|
Dynamic(String, usize), // format spec, arg index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_format_string(fmt: &str) -> Vec<FormatSegment> {
|
fn parse_format_string(fmt: &str) -> Vec<FormatSegment> {
|
||||||
let mut segments = Vec::new();
|
let mut segments = Vec::new();
|
||||||
let mut current_static = String::new();
|
let mut current_static = String::new();
|
||||||
let mut chars = fmt.chars().peekable();
|
let mut chars = fmt.chars().peekable();
|
||||||
|
|||||||
+21
-89
@@ -1,50 +1,42 @@
|
|||||||
#![feature(proc_macro_quote)]
|
#![feature(proc_macro_quote)]
|
||||||
#![feature(proc_macro_span)]
|
#![feature(proc_macro_span)]
|
||||||
|
#![allow(dead_code, unused_macros, unused_imports)]
|
||||||
|
|
||||||
|
mod env;
|
||||||
|
mod format_helper;
|
||||||
|
mod proc_impl_switcher;
|
||||||
|
|
||||||
|
mod obfuscate;
|
||||||
|
|
||||||
|
// Types of symbolic reference
|
||||||
|
mod symbolic_aes;
|
||||||
|
mod symbolic_ref;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::parse_macro_input;
|
|
||||||
|
|
||||||
mod format_helper;
|
use proc_impl_switcher::proc_impl;
|
||||||
use format_helper::*;
|
|
||||||
|
|
||||||
mod crypt;
|
|
||||||
|
|
||||||
#[allow(dead_code, unused_imports)]
|
|
||||||
mod no_obfuscate;
|
|
||||||
|
|
||||||
#[allow(dead_code, unused_imports)]
|
|
||||||
mod obfuscate;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "obfuscate"))]
|
|
||||||
use no_obfuscate as obs;
|
|
||||||
#[cfg(feature = "obfuscate")]
|
|
||||||
use obfuscate as obs;
|
|
||||||
|
|
||||||
// String obfuscation
|
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn obs(input: TokenStream) -> TokenStream {
|
pub fn obs(input: TokenStream) -> TokenStream {
|
||||||
obs::xor(input)
|
proc_impl::xor(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn obfuscated_symbol(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn sym_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
obs::aes_fn_name(_attr, item)
|
proc_impl::sym_fn(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn symbol(input: TokenStream) -> TokenStream {
|
pub fn sym(input: TokenStream) -> TokenStream {
|
||||||
obs::aes_str(input)
|
proc_impl::sym(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn junk_asm(input: TokenStream) -> TokenStream {
|
pub fn junk_asm(input: TokenStream) -> TokenStream {
|
||||||
obs::junk_asm(input)
|
proc_impl::junk_asm(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn file_symbol(_input: TokenStream) -> TokenStream {
|
pub fn file_symbol(_input: TokenStream) -> TokenStream {
|
||||||
// Get the call site span to extract file information
|
// Get the call site span to extract file information
|
||||||
@@ -56,72 +48,12 @@ pub fn file_symbol(_input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
// Return as a string literal
|
// Return as a string literal
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
obfuscate::symbol!(#concatted)
|
obfuscate::sym!(#concatted)
|
||||||
};
|
};
|
||||||
// let output = quote! {
|
|
||||||
// #concatted
|
|
||||||
// };
|
|
||||||
output.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn format_obs(input: TokenStream) -> TokenStream {
|
pub fn sym_format(input: TokenStream) -> TokenStream {
|
||||||
let PrintlnArgs { format_str, args } = parse_macro_input!(input as PrintlnArgs);
|
format_helper::sym_format(input)
|
||||||
|
|
||||||
let segments = parse_format_string(&format_str);
|
|
||||||
|
|
||||||
if segments.is_empty() {
|
|
||||||
return quote! {
|
|
||||||
print!("\n")
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut parts = Vec::new();
|
|
||||||
|
|
||||||
for segment in segments {
|
|
||||||
match segment {
|
|
||||||
FormatSegment::Static(text) => {
|
|
||||||
parts.push(quote! {
|
|
||||||
obfuscate::symbol!(#text).to_string()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
FormatSegment::Dynamic(spec, idx) => {
|
|
||||||
if idx >= args.len() {
|
|
||||||
return syn::Error::new(
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
format!("argument {} is missing", idx),
|
|
||||||
)
|
|
||||||
.to_compile_error()
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let arg = &args[idx];
|
|
||||||
let fmt_spec = if spec.is_empty() {
|
|
||||||
quote! { "{}" }
|
|
||||||
} else {
|
|
||||||
let full_spec = format!("{{{}}}", spec);
|
|
||||||
quote! { #full_spec }
|
|
||||||
};
|
|
||||||
|
|
||||||
// quote! {
|
|
||||||
// println!(#fmt_spec, #arg);
|
|
||||||
// }
|
|
||||||
parts.push(quote! {
|
|
||||||
format!(#fmt_spec, #arg)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(quote! {
|
|
||||||
{
|
|
||||||
let mut string = String::new();
|
|
||||||
#(
|
|
||||||
string.push_str(&#parts);
|
|
||||||
)*
|
|
||||||
string
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::{ItemFn, LitStr, parse_macro_input};
|
|
||||||
|
|
||||||
pub fn xor(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as LitStr);
|
|
||||||
|
|
||||||
(quote::quote! {
|
|
||||||
String::from(#input)
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
pub fn aes_fn_name(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
||||||
let func = parse_macro_input!(item as ItemFn);
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
#func
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn aes_str(input: TokenStream) -> TokenStream {
|
|
||||||
input
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn junk_asm(_input: TokenStream) -> TokenStream {
|
|
||||||
TokenStream::new()
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,5 @@
|
|||||||
mod obs_junk_asm;
|
mod obs_junk_asm;
|
||||||
mod obs_xor;
|
mod obs_xor;
|
||||||
mod sym_aes_strings;
|
|
||||||
|
|
||||||
pub use obs_junk_asm::junk_asm;
|
pub use obs_junk_asm::junk_asm;
|
||||||
pub use obs_xor::xor;
|
pub use obs_xor::xor;
|
||||||
pub use sym_aes_strings::*;
|
|
||||||
|
|
||||||
use crate::crypt::{BACKUP_ENV_KEY, ENV_KEY_NAME};
|
|
||||||
|
|
||||||
fn get_encryption_key() -> String {
|
|
||||||
std::env::var(ENV_KEY_NAME).unwrap_or({
|
|
||||||
println!("Using default encryption key!");
|
|
||||||
BACKUP_ENV_KEY.to_owned()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn
|
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ pub fn xor(input: TokenStream) -> TokenStream {
|
|||||||
return TokenStream::from(quote! { String::new() });
|
return TokenStream::from(quote! { String::new() });
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Obfuscated Branch Logic ---
|
|
||||||
// This code runs at compile-time
|
|
||||||
|
|
||||||
let str_bytes = original_str.as_bytes();
|
let str_bytes = original_str.as_bytes();
|
||||||
let len = str_bytes.len();
|
let len = str_bytes.len();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/// Call some other function
|
||||||
|
macro_rules! passtrough {
|
||||||
|
($name:tt, $ref:expr) => {
|
||||||
|
pub fn $name(input: TokenStream) -> TokenStream {
|
||||||
|
$ref(input)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Just return the underlying string
|
||||||
|
macro_rules! unwrap_string {
|
||||||
|
($func:tt) => {
|
||||||
|
pub fn $func(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as LitStr);
|
||||||
|
|
||||||
|
(quote::quote! {
|
||||||
|
#input
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete the content
|
||||||
|
macro_rules! delete {
|
||||||
|
($func:tt) => {
|
||||||
|
pub fn $func(_: TokenStream) -> TokenStream {
|
||||||
|
(quote::quote! {}).into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "obfuscate_aes"), not(feature = "obfuscate_ref")))]
|
||||||
|
pub mod proc_impl {
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use syn::{LitStr, parse_macro_input};
|
||||||
|
|
||||||
|
unwrap_string!(xor);
|
||||||
|
delete!(junk_asm);
|
||||||
|
|
||||||
|
unwrap_string!(sym);
|
||||||
|
unwrap_string!(sym_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "obfuscate_aes")]
|
||||||
|
pub mod proc_impl {
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
passtrough!(xor, crate::obfuscate::xor);
|
||||||
|
passtrough!(junk_asm, crate::obfuscate::junk_asm);
|
||||||
|
|
||||||
|
passtrough!(sym, crate::symbolic_aes::aes_str);
|
||||||
|
passtrough!(sym_fn, crate::symbolic_aes::aes_fn_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "obfuscate_ref")]
|
||||||
|
pub mod proc_impl {
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use syn::{LitStr, parse_macro_input};
|
||||||
|
|
||||||
|
passtrough!(xor, crate::obfuscate::xor);
|
||||||
|
delete!(junk_asm);
|
||||||
|
|
||||||
|
passtrough!(sym, crate::symbolic_ref::sym_ref);
|
||||||
|
passtrough!(sym_fn, crate::symbolic_ref::sym_ref_fn);
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
use crate::crypt::{BACKUP_ENV_KEY, ENV_KEY_NAME, STATIC_IV, aes_encrypt::encrypt_aes_lines};
|
use base62::{STATIC_IV, encrypt_aes_lines};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{ItemFn, LitStr, parse_macro_input};
|
use syn::{ItemFn, LitStr, parse_macro_input};
|
||||||
|
|
||||||
use crate::obfuscate::get_encryption_key;
|
use crate::env::get_encryption_key;
|
||||||
|
|
||||||
/// Obfuscate function names by encrypting in AES
|
/// Obfuscate function names by encrypting in AES
|
||||||
pub fn aes_fn_name(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn aes_fn_name(item: TokenStream) -> TokenStream {
|
||||||
// Parse the input function
|
// Parse the input function
|
||||||
|
|
||||||
let func = parse_macro_input!(item as ItemFn);
|
let func = parse_macro_input!(item as ItemFn);
|
||||||
|
|
||||||
// Get the original function name
|
// Get the original function name
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use base62::{Base62, hash};
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{ItemFn, LitStr, parse_macro_input};
|
||||||
|
|
||||||
|
use crate::env::get_encryption_key;
|
||||||
|
|
||||||
|
static mut SYM_COUNTER: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
pub fn get_symbol_number() -> usize {
|
||||||
|
unsafe { SYM_COUNTER.len() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
pub fn get_symbol(text: &str) -> usize {
|
||||||
|
unsafe {
|
||||||
|
if let Some(n) = SYM_COUNTER.iter().position(|r| r == text) {
|
||||||
|
n
|
||||||
|
} else {
|
||||||
|
SYM_COUNTER.push(text.to_string());
|
||||||
|
|
||||||
|
SYM_COUNTER.len() - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ref_string(input: String) -> String {
|
||||||
|
let n = get_symbol(&input);
|
||||||
|
|
||||||
|
let data = base62::encode_usize(n);
|
||||||
|
let key = hash(&get_encryption_key().as_bytes());
|
||||||
|
|
||||||
|
let encoded = format!("_{}_", Base62::encode_full(&data, &key));
|
||||||
|
|
||||||
|
println!("Aliased '{}' as '{encoded}'", input);
|
||||||
|
|
||||||
|
encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sym_ref(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input as a string literal
|
||||||
|
let lit_str = parse_macro_input!(input as LitStr);
|
||||||
|
let original_name = lit_str.value();
|
||||||
|
|
||||||
|
let encoded = ref_string(original_name);
|
||||||
|
|
||||||
|
// Expand to a static string literal
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
#encoded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sym_ref_fn(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input function
|
||||||
|
let func = parse_macro_input!(input as ItemFn);
|
||||||
|
|
||||||
|
// Get the original function name
|
||||||
|
let fn_name = func.sig.ident.to_string();
|
||||||
|
|
||||||
|
// Generate the new, obfuscated name
|
||||||
|
let obfuscated_name = ref_string(fn_name);
|
||||||
|
|
||||||
|
// Create a new string literal for the name
|
||||||
|
let new_name_lit = LitStr::new(&obfuscated_name, func.sig.ident.span());
|
||||||
|
|
||||||
|
// Re-build the function, but add #[no_mangle]
|
||||||
|
// and rename the *exported* symbol via #[export_name]
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
#[unsafe(export_name = #new_name_lit)]
|
||||||
|
#func
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ edition = "2024"
|
|||||||
default = ["log"]
|
default = ["log"]
|
||||||
log = ["unshell/log"]
|
log = ["unshell/log"]
|
||||||
log_debug = ["unshell/log_debug"]
|
log_debug = ["unshell/log_debug"]
|
||||||
obfuscate = ["unshell/obfuscate"]
|
obfuscate = ["unshell/obfuscate_ref"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
unshell.path = "../"
|
unshell.path = "../"
|
||||||
|
|||||||
+26
-24
@@ -1,44 +1,46 @@
|
|||||||
use unshell::{
|
#![macro_use]
|
||||||
Value, info,
|
extern crate unshell;
|
||||||
logger::{Record, log},
|
|
||||||
obfuscate::{junk_asm, symbol},
|
use unshell::{info, obfuscate::sym, tree::Tree};
|
||||||
tree::{Tree, TreeElement, symbols},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// PrettyLogger::init();
|
||||||
|
|
||||||
let mut manager = Tree::new();
|
let mut manager = Tree::new();
|
||||||
manager.init_logger();
|
manager.init_logger();
|
||||||
|
|
||||||
|
println!("{}", sym!("TEST"));
|
||||||
|
|
||||||
info!("Test thing!");
|
info!("Test thing!");
|
||||||
info!("Test thing!");
|
info!("Test thing!");
|
||||||
|
|
||||||
loop {
|
// loop {
|
||||||
if test123(&mut manager) {
|
// if test123(&mut manager) {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// println!("Test");
|
// println!("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test123(manager: &mut Tree) -> bool {
|
// fn test123(manager: &mut Tree) -> bool {
|
||||||
let result = manager.send_message(
|
// let result = manager.send_message(
|
||||||
Value::String(symbol!("Logger").to_string()),
|
// Value::String(sym!("Logger").to_string()),
|
||||||
Value::String(symbols::CMD_GET.to_string()),
|
// Value::String(symbols::CMD_GET.to_string()),
|
||||||
);
|
// );
|
||||||
|
|
||||||
junk_asm!(20.);
|
// junk_asm!(20.);
|
||||||
|
|
||||||
let is_null = result.is_null();
|
// let is_null = result.is_null();
|
||||||
|
|
||||||
if let Ok(result) = serde_json::from_value::<Record>(result) {
|
// // if let Ok(result) = serde_json::from_value::<Record>(result) {
|
||||||
log(&result);
|
// // log(&result);
|
||||||
}
|
// // }
|
||||||
|
|
||||||
is_null
|
// is_null
|
||||||
|
|
||||||
// println!("Logger: {}", result);
|
// // println!("Logger: {}", result);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// use std::{any::Any, collections::HashMap, fs::File, io::Read};
|
// use std::{any::Any, collections::HashMap, fs::File, io::Read};
|
||||||
|
|
||||||
|
|||||||
Generated
-1937
File diff suppressed because it is too large
Load Diff
@@ -1,31 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ush-server"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
include.workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["log_debug"]
|
|
||||||
log = ["unshell/log"]
|
|
||||||
log_debug = ["unshell/log_debug"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
unshell = { path = "../" }
|
|
||||||
# ush-manager = { path = "../unshell-manager" }
|
|
||||||
|
|
||||||
chrono = { workspace = true }
|
|
||||||
toml = { workspace = true }
|
|
||||||
static_init = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
|
|
||||||
sled = "0.34.7"
|
|
||||||
|
|
||||||
clap = {version = "4.5.53", features = ["derive"]}
|
|
||||||
axum = "0.8.7"
|
|
||||||
axum-extra = {version="0.12.2", features = ["typed-header"]}
|
|
||||||
tokio = {version="1.48.0", features = ["full"] }
|
|
||||||
|
|
||||||
jsonwebtoken = {version = "10.2.0", features = ["aws_lc_rs"]}
|
|
||||||
bcrypt = "0.17.1"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
GET /api/interface/*/some_config -> Returns the interface and struct at that location
|
|
||||||
|
|
||||||
GET /api/interface/*/some_folder -> Returns the list of sub-folders and interfaces at that location
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
use axum::{
|
|
||||||
Extension, Json, Router,
|
|
||||||
extract::{Path, State},
|
|
||||||
middleware,
|
|
||||||
routing::{get, post},
|
|
||||||
};
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use unshell::{debug, info};
|
|
||||||
|
|
||||||
// axum_extra::
|
|
||||||
|
|
||||||
use crate::{auth, auth::structs::CurrentUser, logger::Logger, server::Server};
|
|
||||||
|
|
||||||
macro_rules! route_get {
|
|
||||||
($router:expr, $path:expr, $func:expr) => {{
|
|
||||||
{
|
|
||||||
$router.route(
|
|
||||||
$path,
|
|
||||||
get($func).layer(middleware::from_fn(auth::authorize)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! route_post {
|
|
||||||
($router:expr, $path:expr, $func:expr) => {{
|
|
||||||
{
|
|
||||||
$router.route(
|
|
||||||
$path,
|
|
||||||
post($func).layer(middleware::from_fn(auth::authorize)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn start_api(address: &str, server: Server) {
|
|
||||||
let listener = TcpListener::bind(address)
|
|
||||||
.await
|
|
||||||
.expect("Unable to start listener");
|
|
||||||
|
|
||||||
info!("Listening on {}", listener.local_addr().unwrap());
|
|
||||||
|
|
||||||
let mut router = Router::new().route("/api/auth", post(auth::sign_in));
|
|
||||||
|
|
||||||
router = route_trees(router);
|
|
||||||
|
|
||||||
router = route_get!(router, "/api/log/{*offset}", Logger::poll_logs_api);
|
|
||||||
router = route_get!(router, "/api/trees", Server::get_trees_api);
|
|
||||||
router = route_get!(router, "/api/keys/{*path}", Server::all_tree_keys_api);
|
|
||||||
router = route_get!(router, "/api/values/{*path}", Server::all_tree_values_api);
|
|
||||||
|
|
||||||
router = route_get!(router, "/api/interface/", Server::get_tree2_root);
|
|
||||||
router = route_get!(router, "/api/interface/{*path}", Server::get_tree2);
|
|
||||||
|
|
||||||
// router = router.route("/api/interface", get(Server::get_tree2_root));
|
|
||||||
|
|
||||||
// router = router.route("/api/interface/{*path}", post(Server::post_tree2));
|
|
||||||
|
|
||||||
router = route_post!(router, "/api/interface/{*path}", Server::post_tree2);
|
|
||||||
|
|
||||||
// router = route_get_log(router);
|
|
||||||
|
|
||||||
axum::serve(listener, router.with_state(server))
|
|
||||||
.await
|
|
||||||
.expect("Error serving application");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through all trees and add /api/<tree>/<path> POST aand GET listeners for them
|
|
||||||
fn route_trees(mut router: Router<Server>) -> Router<Server> {
|
|
||||||
for tree in crate::DATABASE_TREES.iter() {
|
|
||||||
router = router
|
|
||||||
// Route GET requests to this tree
|
|
||||||
.route(
|
|
||||||
&format!("/api/{}/{{*path}}", tree),
|
|
||||||
get(
|
|
||||||
async |State(server): State<Server>,
|
|
||||||
Path(path): Path<String>,
|
|
||||||
Extension(_): Extension<CurrentUser>| {
|
|
||||||
let result = server.get_value(tree, &path);
|
|
||||||
debug!("GET /api/{}/{}", tree.to_string(), path);
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.layer(middleware::from_fn(auth::authorize)),
|
|
||||||
)
|
|
||||||
// Route POST requests to this tree
|
|
||||||
.route(
|
|
||||||
&format!("/api/{}/{{*path}}", tree),
|
|
||||||
post(
|
|
||||||
async |State(server): State<Server>,
|
|
||||||
Path(path): Path<String>,
|
|
||||||
Extension(_): Extension<CurrentUser>,
|
|
||||||
body: String| {
|
|
||||||
let result = server.put_value(tree, &path, &body);
|
|
||||||
debug!("POST /api/{}/{}", tree.to_string(), path);
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.layer(middleware::from_fn(auth::authorize)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
router
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
pub mod structs;
|
|
||||||
|
|
||||||
use axum::{
|
|
||||||
body::Body,
|
|
||||||
extract::{Json, Request},
|
|
||||||
http::{self, Response, StatusCode},
|
|
||||||
middleware::Next,
|
|
||||||
};
|
|
||||||
use bcrypt::{DEFAULT_COST, hash, verify};
|
|
||||||
use chrono::Utc;
|
|
||||||
use jsonwebtoken::{Header, TokenData, Validation, decode, encode};
|
|
||||||
use serde_json::{Value, json};
|
|
||||||
use unshell::{debug, info};
|
|
||||||
|
|
||||||
use crate::{EXPIRE_DURATION, JWT_DECODING_KEY, JWT_ENCODING_KEY};
|
|
||||||
|
|
||||||
use structs::{AuthError, Cliams, CurrentUser, SignInData};
|
|
||||||
|
|
||||||
pub fn hash_password(password: &str) -> Result<String, bcrypt::BcryptError> {
|
|
||||||
let hash = hash(password, DEFAULT_COST)?;
|
|
||||||
Ok(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode_jwt(email: String) -> Result<(String, usize), StatusCode> {
|
|
||||||
let now = Utc::now();
|
|
||||||
let exp = (now + EXPIRE_DURATION).timestamp() as usize;
|
|
||||||
let iat = now.timestamp() as usize;
|
|
||||||
|
|
||||||
let claim = Cliams { iat, exp, email };
|
|
||||||
|
|
||||||
let token = encode(&Header::default(), &claim, &JWT_ENCODING_KEY)
|
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
||||||
|
|
||||||
Ok((token, exp))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode_jwt(jwt: String) -> Result<TokenData<Cliams>, StatusCode> {
|
|
||||||
decode(&jwt, &JWT_DECODING_KEY, &Validation::default())
|
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn authorize(mut req: Request, next: Next) -> Result<Response<Body>, AuthError> {
|
|
||||||
let auth_header = req.headers_mut().get(http::header::AUTHORIZATION);
|
|
||||||
|
|
||||||
let auth_header = match auth_header {
|
|
||||||
Some(header) => header.to_str().map_err(|_| AuthError {
|
|
||||||
message: "Empty header is not allowed".to_string(),
|
|
||||||
status_code: StatusCode::FORBIDDEN,
|
|
||||||
})?,
|
|
||||||
None => {
|
|
||||||
return Err(AuthError {
|
|
||||||
message: "Please add the JWT token to the header".to_string(),
|
|
||||||
status_code: StatusCode::FORBIDDEN,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut header = auth_header.split_whitespace();
|
|
||||||
|
|
||||||
let (_, token) = (header.next(), header.next());
|
|
||||||
|
|
||||||
let _token_data: TokenData<Cliams> = match decode_jwt(token.unwrap().to_string()) {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(AuthError {
|
|
||||||
message: "Invalid Session".to_string(),
|
|
||||||
status_code: StatusCode::UNAUTHORIZED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// // Fetch the user details from the database
|
|
||||||
// let current_user = match retrieve_user_by_email(&token_data.claims.email) {
|
|
||||||
// Some(user) => user,
|
|
||||||
// None => {
|
|
||||||
// return Err(AuthError {
|
|
||||||
// message: "Unauthorized".to_string(),
|
|
||||||
// status_code: StatusCode::UNAUTHORIZED,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// req.extensions_mut().insert(current_user);
|
|
||||||
Ok(next.run(req).await)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn sign_in(Json(user_data): Json<SignInData>) -> Result<Json<Value>, StatusCode> {
|
|
||||||
// 1. Retrieve user from the database
|
|
||||||
let user = match retrieve_user_by_email(&user_data.username) {
|
|
||||||
Some(user) => user,
|
|
||||||
None => {
|
|
||||||
debug!(
|
|
||||||
"Denied user {}: Could not find user data",
|
|
||||||
user_data.username
|
|
||||||
);
|
|
||||||
return Err(StatusCode::UNAUTHORIZED);
|
|
||||||
} // User not found
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2. Compare the password
|
|
||||||
if !verify(&user_data.password, &user.password_hash)
|
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
||||||
// Handle bcrypt errors
|
|
||||||
{
|
|
||||||
debug!("Denied user {}: Incorrect password hash", user.username);
|
|
||||||
return Err(StatusCode::UNAUTHORIZED); // Wrong password
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Authenticated user {} for {}",
|
|
||||||
user_data.username, EXPIRE_DURATION
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. Generate JWT
|
|
||||||
let (token, experation) =
|
|
||||||
encode_jwt(user.username).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
||||||
|
|
||||||
// 4. Return the token
|
|
||||||
Ok(Json(json!({
|
|
||||||
"token": token,
|
|
||||||
"expiration": experation,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn retrieve_user_by_email(_email: &str) -> Option<CurrentUser> {
|
|
||||||
let current_user: CurrentUser = CurrentUser {
|
|
||||||
username: "foo".to_string(),
|
|
||||||
password_hash: hash_password("bar").unwrap(),
|
|
||||||
};
|
|
||||||
Some(current_user)
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
use axum::{
|
|
||||||
Json,
|
|
||||||
body::Body,
|
|
||||||
http::{Response, StatusCode},
|
|
||||||
response::IntoResponse,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct SignInData {
|
|
||||||
pub username: String,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CurrentUser {
|
|
||||||
pub username: String,
|
|
||||||
pub password_hash: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Cliams {
|
|
||||||
// pub exp: u128,
|
|
||||||
// pub iat: u128,
|
|
||||||
//
|
|
||||||
pub exp: usize,
|
|
||||||
pub iat: usize,
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AuthError {
|
|
||||||
pub message: String,
|
|
||||||
pub status_code: StatusCode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoResponse for AuthError {
|
|
||||||
fn into_response(self) -> Response<Body> {
|
|
||||||
let body = Json(json!({
|
|
||||||
"error": self.message,
|
|
||||||
}));
|
|
||||||
|
|
||||||
(self.status_code, body).into_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
mod api;
|
|
||||||
mod auth;
|
|
||||||
// mod config;
|
|
||||||
pub mod logger;
|
|
||||||
mod server;
|
|
||||||
|
|
||||||
// use math
|
|
||||||
|
|
||||||
pub use server::Server;
|
|
||||||
|
|
||||||
use static_init::dynamic;
|
|
||||||
|
|
||||||
#[static_init::dynamic]
|
|
||||||
pub static DATABASE_TREES: Vec<&'static str> = vec!["users"];
|
|
||||||
|
|
||||||
#[static_init::dynamic]
|
|
||||||
pub static DEFAULT_HOST: String = "localhost".to_string();
|
|
||||||
#[static_init::dynamic]
|
|
||||||
pub static DATABASE_NAME: String = "database".to_string();
|
|
||||||
|
|
||||||
#[static_init::dynamic]
|
|
||||||
pub static SERVER_CONFIG: unshell_manager::PayloadConfig = unshell_manager::PayloadConfig {
|
|
||||||
id: "Server",
|
|
||||||
components: Vec::new(),
|
|
||||||
runtime_config: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constants for server config
|
|
||||||
pub use api::start_api;
|
|
||||||
use chrono::Duration;
|
|
||||||
use jsonwebtoken::{DecodingKey, EncodingKey};
|
|
||||||
|
|
||||||
static EXPIRE_DURATION: Duration = Duration::hours(12);
|
|
||||||
|
|
||||||
#[dynamic]
|
|
||||||
|
|
||||||
static JWT_SECRET: String = {
|
|
||||||
if let Ok(env_secret) = std::env::var("JWT_SECRET") {
|
|
||||||
env_secret
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
r#"
|
|
||||||
##############
|
|
||||||
# WARNING: You are using the default JWT secret, used for creating user sessions
|
|
||||||
# With this default key, anyone can login as any user.
|
|
||||||
##############"#
|
|
||||||
);
|
|
||||||
"DEFAULT_SECRET".to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// std::env::var("JWT_SECRET").unwrap_or(|| -> String {
|
|
||||||
// return "TEST".to_string();
|
|
||||||
// }());
|
|
||||||
|
|
||||||
#[dynamic]
|
|
||||||
static JWT_ENCODING_KEY: EncodingKey = EncodingKey::from_secret(JWT_SECRET.as_bytes());
|
|
||||||
#[dynamic]
|
|
||||||
static JWT_DECODING_KEY: DecodingKey = DecodingKey::from_secret(JWT_SECRET.as_bytes());
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
use axum::extract::{Path, State};
|
|
||||||
use axum::{Extension, Json};
|
|
||||||
use chrono::Local;
|
|
||||||
use std::fs::{self, File, OpenOptions};
|
|
||||||
use std::io::{BufRead, BufReader, Write};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use unshell::debug;
|
|
||||||
|
|
||||||
use crate::Server;
|
|
||||||
use crate::auth::structs::CurrentUser;
|
|
||||||
|
|
||||||
const LOG_DIR: &str = "logs";
|
|
||||||
|
|
||||||
const LOG_COUNT: usize = 100;
|
|
||||||
|
|
||||||
/// The full path to the log file.
|
|
||||||
/// Initialized once based on the startup time.
|
|
||||||
#[static_init::dynamic]
|
|
||||||
static LOG_FILE_PATH: PathBuf = {
|
|
||||||
let log_dir_path = PathBuf::from(LOG_DIR);
|
|
||||||
|
|
||||||
if let Err(e) = fs::create_dir_all(&log_dir_path) {
|
|
||||||
eprintln!("Error creating log directory {:?}: {}", log_dir_path, e);
|
|
||||||
|
|
||||||
panic!("Failed to initialize log directory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let now = Local::now();
|
|
||||||
let filename = format!("{}.log", now.format("%Y%m%d_%H%M%S"));
|
|
||||||
|
|
||||||
log_dir_path.join(filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A static utility module for logging operations.
|
|
||||||
pub struct Logger;
|
|
||||||
|
|
||||||
impl Logger {
|
|
||||||
pub fn log(message: String) {
|
|
||||||
let log_line = format!("{}\n", message);
|
|
||||||
|
|
||||||
match OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.append(true)
|
|
||||||
.open(&*LOG_FILE_PATH)
|
|
||||||
{
|
|
||||||
Ok(mut file) => {
|
|
||||||
// 3. Write the log line to the file
|
|
||||||
if let Err(e) = file.write_all(log_line.as_bytes()) {
|
|
||||||
eprintln!("Error writing log to file {:?}: {}", *LOG_FILE_PATH, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error opening log file {:?}: {}", *LOG_FILE_PATH, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_logs(offset: usize) -> Vec<String> {
|
|
||||||
match File::open(&*LOG_FILE_PATH) {
|
|
||||||
Ok(file) => {
|
|
||||||
let reader = BufReader::new(file);
|
|
||||||
let lines: Vec<String> = reader
|
|
||||||
.lines()
|
|
||||||
.filter_map(|line| line.ok()) // Ignore lines that fail to read
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let total_lines = lines.len();
|
|
||||||
if offset >= total_lines {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let start_index = total_lines
|
|
||||||
.checked_sub(offset)
|
|
||||||
.and_then(|i| i.checked_sub(LOG_COUNT))
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
let end_index = total_lines.checked_sub(offset).unwrap_or(total_lines);
|
|
||||||
|
|
||||||
let slice = &lines[start_index..end_index];
|
|
||||||
|
|
||||||
slice.iter().cloned().collect()
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error reading log file {:?}: {}", *LOG_FILE_PATH, e);
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn poll_logs_api(
|
|
||||||
State(_): State<Server>,
|
|
||||||
Extension(_): Extension<CurrentUser>,
|
|
||||||
Path(offset): Path<usize>,
|
|
||||||
) -> axum::Json<serde_json::Value> {
|
|
||||||
debug!("GET /api/log/{}", offset);
|
|
||||||
let result = Self::poll_logs(offset);
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
use std::{error::Error, path::PathBuf};
|
|
||||||
|
|
||||||
use unshell_server::{Server, start_api};
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use unshell_server::{DATABASE_NAME, DEFAULT_HOST};
|
|
||||||
|
|
||||||
/// A fictional versioning CLI
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
#[command(name = "unshell-server")]
|
|
||||||
#[command(about = "UnShell server", long_about = None)]
|
|
||||||
pub struct Args {
|
|
||||||
/// Host to listen on
|
|
||||||
#[clap(long, default_value_t = DEFAULT_HOST.clone())]
|
|
||||||
host: String,
|
|
||||||
|
|
||||||
/// Port to listen
|
|
||||||
#[arg(short, long, default_value_t = 3000)]
|
|
||||||
port: usize,
|
|
||||||
|
|
||||||
/// Name of database folder
|
|
||||||
#[clap(short, long, default_value_t = DATABASE_NAME.clone())]
|
|
||||||
database_name: String,
|
|
||||||
|
|
||||||
/// Load config from path
|
|
||||||
#[clap(short, long, value_parser)]
|
|
||||||
pub config: Vec<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
unshell::logger::PrettyLogger::init_output(|message| {
|
|
||||||
if let Ok(json) = serde_json::to_string(message) {
|
|
||||||
unshell_server::logger::Logger::log(json);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let database = Server::new(args.config, args.database_name)?;
|
|
||||||
|
|
||||||
start_api(&format!("{}:{}", args.host, args.port), database).await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use crate::Server;
|
|
||||||
|
|
||||||
impl Server {
|
|
||||||
pub fn get_blobs(&self) -> Result<Vec<Value>, String> {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use axum::{
|
|
||||||
Extension, Json,
|
|
||||||
extract::{Path, State},
|
|
||||||
};
|
|
||||||
use serde_json::Value;
|
|
||||||
use sled::Tree;
|
|
||||||
use unshell::{debug, error};
|
|
||||||
|
|
||||||
use crate::{auth::structs::CurrentUser, server::Server};
|
|
||||||
|
|
||||||
impl Server {
|
|
||||||
fn get_tree(&self, tree_name: &str) -> Result<Tree, String> {
|
|
||||||
self.db.open_tree(tree_name).map_err(|e| {
|
|
||||||
error!("DB Failed to open tree: {}", e);
|
|
||||||
"Internal server error".to_string()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_trees_api(State(server): State<Server>) -> Json<Value> {
|
|
||||||
debug!("GET tree list");
|
|
||||||
|
|
||||||
let result = server
|
|
||||||
.db
|
|
||||||
.tree_names()
|
|
||||||
.iter()
|
|
||||||
.map(|n| String::from_utf8_lossy(&n.to_vec()).to_string())
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put_value(&self, tree_name: &str, key: &str, value: &str) -> Result<(), String> {
|
|
||||||
match self.get_tree(tree_name)?.insert(key, value) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to load '{}' from database: {}", key, e);
|
|
||||||
Err("Internal server error".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_value(&self, tree_name: &str, key: &str) -> Result<String, String> {
|
|
||||||
match self.get_tree(tree_name)?.get(key) {
|
|
||||||
Ok(v) => match v {
|
|
||||||
Some(v) => Ok(String::from_utf8_lossy(&v.to_vec()).to_string()),
|
|
||||||
None => Err(format!("Could not find key '{}'", key)),
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to load '{}' from database: {}", key, e);
|
|
||||||
Err("Internal server error".to_string())
|
|
||||||
// Err(e.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_keys(&self, tree_name: &str) -> Result<Vec<String>, String> {
|
|
||||||
Ok(self
|
|
||||||
.get_tree(tree_name)?
|
|
||||||
.iter()
|
|
||||||
.keys()
|
|
||||||
.map(|key| {
|
|
||||||
String::from_utf8_lossy(&key.expect("This key should exist").to_vec()).to_string()
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Route the "keys" api for each tree
|
|
||||||
pub async fn all_tree_keys_api(
|
|
||||||
State(server): State<Server>,
|
|
||||||
Path(tree_name): Path<String>,
|
|
||||||
Extension(_): Extension<CurrentUser>,
|
|
||||||
) -> Json<Value> {
|
|
||||||
let result = server.get_keys(&tree_name);
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Route the "values" api to get all the values for each tree
|
|
||||||
pub async fn all_tree_values_api(
|
|
||||||
State(server): State<Server>,
|
|
||||||
Path(tree_name): Path<String>,
|
|
||||||
Extension(_): Extension<CurrentUser>,
|
|
||||||
) -> Json<Value> {
|
|
||||||
let result = || -> Result<HashMap<String, String>, String> {
|
|
||||||
Ok(server
|
|
||||||
.get_keys(&tree_name)?
|
|
||||||
.iter()
|
|
||||||
.map(|key| -> Result<(String, String), String> {
|
|
||||||
Ok((key.clone(), server.get_value(&tree_name, &key)?))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<(String, String)>, String>>()?
|
|
||||||
.into_iter()
|
|
||||||
.collect::<HashMap<String, String>>())
|
|
||||||
}();
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
use std::{
|
|
||||||
path::PathBuf,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use unshell::{
|
|
||||||
ModuleError, Result,
|
|
||||||
config::{ConfigStructField, Tree, TreeMessage, config_struct::Config},
|
|
||||||
};
|
|
||||||
use unshell_manager::Manager;
|
|
||||||
|
|
||||||
mod blobs;
|
|
||||||
mod database;
|
|
||||||
mod tree2;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Server {
|
|
||||||
// pub component_configs: Vec<crate::config::ComponentState>,
|
|
||||||
// pub interface: InterfaceWrapper,
|
|
||||||
pub manager: Arc<Mutex<Manager>>,
|
|
||||||
pub db: sled::Db,
|
|
||||||
// pub tree: Tree2,
|
|
||||||
test_thing: Arc<Mutex<Config>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Server {
|
|
||||||
pub fn new(_config_paths: Vec<PathBuf>, database: String) -> Result<Self> {
|
|
||||||
// let mut component_configs: Vec<crate::config::ComponentState> = Vec::new(1);
|
|
||||||
|
|
||||||
// for config in &config_paths {
|
|
||||||
// component_configs.extend(crate::config::load_config(config)?);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
// component_configs,
|
|
||||||
manager: Manager::start(&crate::SERVER_CONFIG, Vec::new()),
|
|
||||||
db: sled::open(database).map_err(|e| ModuleError::DatabaseError(e.to_string()))?,
|
|
||||||
|
|
||||||
test_thing: Arc::new(Mutex::new(Config::new(vec![
|
|
||||||
ConfigStructField::Header("Test Heading".into()),
|
|
||||||
ConfigStructField::Text("Test Texttttttttttttttt".into()),
|
|
||||||
ConfigStructField::String {
|
|
||||||
default: "Test Texttttttttttttttt".into(),
|
|
||||||
max_length: None,
|
|
||||||
protected: true,
|
|
||||||
},
|
|
||||||
ConfigStructField::String {
|
|
||||||
default: "Test ".into(),
|
|
||||||
max_length: Some(15),
|
|
||||||
protected: false,
|
|
||||||
},
|
|
||||||
]))),
|
|
||||||
// tree: Tree2::default(),
|
|
||||||
// interface: get_test_interface(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tree for Server {
|
|
||||||
fn is_folder() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_children_string(&self) -> Vec<String> {
|
|
||||||
vec!["connection_count".into()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select_child(&mut self, child: &str, message: TreeMessage) -> Result<TreeMessage> {
|
|
||||||
match child {
|
|
||||||
"connection_count" => self.test_thing.lock().unwrap().get(message),
|
|
||||||
_ => Err("No such child".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Server {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.db.flush().expect("Failed to flush database on drop");
|
|
||||||
// Manager::join(self.manager.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
use axum::{
|
|
||||||
Json,
|
|
||||||
extract::{Path, State},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde_json::Value;
|
|
||||||
use unshell::{
|
|
||||||
ModuleError,
|
|
||||||
config::{Tree, TreeMessage},
|
|
||||||
debug,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::Server;
|
|
||||||
|
|
||||||
impl Server {
|
|
||||||
pub async fn get_tree2_root(
|
|
||||||
State(server): State<Server>,
|
|
||||||
// Extension(extension): Extension<CurrentUser>,
|
|
||||||
) -> Json<Value> {
|
|
||||||
Self::get_tree2(State(server), Path("".into())).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_tree2(
|
|
||||||
State(mut server): State<Server>,
|
|
||||||
Path(path): Path<String>,
|
|
||||||
// Extension(_): Extension<CurrentUser>,
|
|
||||||
) -> Json<Value> {
|
|
||||||
debug!("GET /api/interface/{}", path);
|
|
||||||
|
|
||||||
let result = server
|
|
||||||
.get(&path, TreeMessage::RequestStructAndValue)
|
|
||||||
.map_err(|e| ModuleError::CryptError(e.to_string()));
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn post_tree2(
|
|
||||||
State(mut server): State<Server>,
|
|
||||||
Path(path): Path<String>,
|
|
||||||
// Extension(_): Extension<CurrentUser>,
|
|
||||||
Json(tree_message): Json<TreeMessage>,
|
|
||||||
) -> Json<Value> {
|
|
||||||
debug!("POST /api/interface/");
|
|
||||||
|
|
||||||
// Json(Value::Null)
|
|
||||||
|
|
||||||
let result = server
|
|
||||||
.get(&path, tree_message)
|
|
||||||
.map_err(|e| ModuleError::CryptError(e.to_string()));
|
|
||||||
|
|
||||||
Json(serde_json::to_value(result).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user