From a3da9fb9ac04805b1be40db5397ce9f0fa8db61b Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sun, 5 Jan 2025 04:33:47 +0100 Subject: [PATCH] refactor + ui --- installs/full.toml | 84 +++++++++++++++ installs/testinstall.toml | 57 ---------- src/config.rs | 17 ++- src/install/ssh.rs | 2 +- src/main.rs | 7 +- src/print.rs | 213 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 318 insertions(+), 62 deletions(-) create mode 100644 installs/full.toml delete mode 100644 installs/testinstall.toml create mode 100644 src/print.rs diff --git a/installs/full.toml b/installs/full.toml new file mode 100644 index 0000000..370e634 --- /dev/null +++ b/installs/full.toml @@ -0,0 +1,84 @@ +# Full Install Template + +# Drive Selection for Install +[drive] +# Device node for the EFI boot filesystem +boot = "/dev/null" + +# Device node for the root filesystem +root = "/dev/null" + +# Root filesystem encryption passphrase +# If this option is set the root filesystem will be encrypted with LUKS +encryption = "password" + +# General configuration +[general] +# Preset +mode = "Desktop" + +# System Locale +locale = "de_DE.UTF-8" + +# Keymap +keyboard_layout = "de" +keyboard_variant = "mac" + +# Timezone +timezone = "Europe/Berlin" + +# Hostname +hostname = "navos" + +[pkg] +# Additional packages +pkg = [ + "nano", + "micro" +] + +# Enable virtualization +virtualization = true + +# Enable docker +docker = true + +# User configuration +# The `[[user]]` directive can be repeated to create multiple users. +[[user]] +# Username +name = "testuser" + +# User password +password = "testpass" + +# Allow user to use `doas` as root +doas_root= true + +# SSH Configuration +# If `[ssh]` is set, openssh will be installed and enabled. +[ssh] +# Config file for sshd +# This file will be copied to the new system +sshd_config = "/etc/ssh/sshd_config" + +# Install a SSH keys +# To set multiple keys, repeat the `[[ssh.key]]` directive. +# Every key will be installed in the users respective `authorized_keys` file. +[[ssh.key]] +# The SSH Key +key = "ssh-rsa ... user@host" + +# The users allowed to login with this key +users = ["testuser", "root"] + +# Ollama Configuration +# If `[ai]` is set, ollama will be installed and enabled. +[ai] +# Install with CUDA supports +gpu = true + +# Pull LLMs +models = [ + "llama3.1:8b" +] diff --git a/installs/testinstall.toml b/installs/testinstall.toml deleted file mode 100644 index de22446..0000000 --- a/installs/testinstall.toml +++ /dev/null @@ -1,57 +0,0 @@ -# Drive Selection for Install -[drive] -boot = "/dev/null" -root = "/dev/null" - -# Use LUKS encryption on root drive -encryption = "password" - -[general] -# Preset -mode = "Desktop" - -# System Locale -locale = "de_DE.UTF-8" - -# Keymap -keyboard_layout = "de" -keyboard_variant = "mac" - -# Timezone -timezone = "Europe/Berlin" - -# Hostname -hostname = "navos" - -[pkg] -# Additional packages -pkg = [ - "nano", - "micro" -] - -# Enable virtualization -virtualization = true - -# Enable docker -docker = true - -[[user]] -# Username -name = "testuser" - -# User password -password = "testpass" - -# Allow user to use doas as root -doas_root= true - -# SSH Configuration -[ssh] -# Config file for sshd -sshd_config = "/etc/ssh/sshd_config" - -# Install a SSH key for the user as `authorized_keys` -[[ssh.key]] -key = "ssh-rsa ... user@host" -users = ["testuser", "root"] diff --git a/src/config.rs b/src/config.rs index 42fbc41..cc093c5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use serde::Deserialize; /// Declarative install configuration @@ -26,7 +28,7 @@ pub struct OllamaConfig { #[derive(Debug, Deserialize)] pub struct SSHConfig { pub sshd_config: Option, - pub key: Vec, + pub key: Option>, } #[derive(Debug, Deserialize)] @@ -90,3 +92,16 @@ pub enum InstallMode { // TODO : Evaluate Kiosk, } + +impl Display for InstallMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InstallMode::Base => f.write_str("Base")?, + InstallMode::Desktop => f.write_str("Desktop")?, + InstallMode::Server => f.write_str("Server")?, + InstallMode::Kiosk => f.write_str("Kiosk")?, + } + + Ok(()) + } +} diff --git a/src/install/ssh.rs b/src/install/ssh.rs index 01f3b89..f1685de 100644 --- a/src/install/ssh.rs +++ b/src/install/ssh.rs @@ -14,7 +14,7 @@ pub fn setup_ssh(conf: Option) { install_file("/mnt/etc/ssh/sshd_config", &content, 0o644); } - for key in &conf.key { + for key in &conf.key.unwrap_or_default() { for user in &key.users { let path = if user == "root" { std::fs::create_dir_all("/root/.ssh").unwrap(); diff --git a/src/main.rs b/src/main.rs index 4f85aed..da97f49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,11 @@ mod create_iso; mod install; mod linux; mod pkg; +mod print; use create_iso::create_iso; use install::install; use linux::is_root; +use print::print_config; use yansi::{Color, Paint}; fn print_status(msg: &str) { @@ -97,9 +99,8 @@ fn main() { } }; - // TODO : Show config - println!("Config: {conf:?}"); - print!("\nDo you want to proceed with this configuration? (yes/no) "); + print_config(&conf); + print!("Do you want to proceed with this configuration? (yes/no) "); let mut input = String::new(); std::io::stdout().flush().expect("Error flushing stdout."); diff --git a/src/print.rs b/src/print.rs new file mode 100644 index 0000000..e8d066f --- /dev/null +++ b/src/print.rs @@ -0,0 +1,213 @@ +use yansi::{Color, Paint}; + +use crate::config::InstallConfig; + +pub fn print_config(conf: &InstallConfig) { + println!("🚀 Install Configuration:"); + + let mut root_info = Tree::new(); + + let mut drive_info = Tree::new(); + drive_info.add_str(format!( + "💾 {} {}", + conf.drive.boot.paint(Color::Red), + "[EFI]".paint(Color::Blue) + )); + drive_info.add_str(format!( + "{} {} {}", + if conf.drive.encryption.is_some() { + "🔒" + } else { + "💾" + }, + conf.drive.root.paint(Color::Red), + "[ROOT]".paint(Color::Blue) + )); + + root_info.add_tree("💾 Drive Selection", drive_info); + + let mut general_info = Tree::new(); + + general_info.add_str(format!( + "💎 {} {}", + "Mode:".paint(Color::Yellow), + conf.general.mode + )); + general_info.add_str(format!( + "🖥️ {} {}", + "Hostname:".paint(Color::Yellow), + conf.general.hostname + )); + general_info.add_str(format!( + "⌨️ {} {} {}", + "Keyboard:".paint(Color::Yellow), + conf.general.keyboard_layout, + conf.general + .keyboard_variant + .as_ref() + .unwrap_or(&String::new()) + )); + general_info.add_str(format!( + "🌍 {} {}", + "Locale:".paint(Color::Yellow), + conf.general.locale + )); + general_info.add_str(format!( + "⌛ {} {}", + "Timezone:".paint(Color::Yellow), + conf.general.timezone + )); + + root_info.add_tree("🔨 General", general_info); + + let mut pkg_info = Tree::new(); + + if conf.pkg.docker { + pkg_info.add_str(format!("🐳 Docker {}", "✔️".paint(Color::Green))); + } + + if conf.pkg.virtualization { + pkg_info.add_str(format!("🎃 Virtualization {}", "✔️".paint(Color::Green))); + } + + if !conf.pkg.pkg.is_empty() { + pkg_info.add_str(format!( + "📦 Additional packages: {}", + conf.pkg.pkg.join(" ") + )); + } + + root_info.add_tree("📦 Packages", pkg_info); + + let mut users_info = Tree::new(); + + for user in &conf.user { + users_info.add_str(format!( + "👤 {} {}", + user.name, + if user.doas_root { "🔑" } else { "" } + )); + } + + root_info.add_tree("🧛 Users", users_info); + + if let Some(ssh_conf) = &conf.ssh { + let mut ssh_info = Tree::new(); + + if let Some(sshd_conf) = &ssh_conf.sshd_config { + ssh_info.add_str(format!("📖 Installing {sshd_conf} as sshd_config")); + } + + if let Some(keys) = &ssh_conf.key { + for key in keys { + let key_origin = key.key.split(" ").nth(2).unwrap_or_default(); + + ssh_info.add_str(format!( + "🔑 Key {} for {}", + key_origin.paint(Color::Blue), + key.users.join(", ").paint(Color::Yellow) + )); + } + } + + root_info.add_tree("🌐 SSH", ssh_info); + } + + if let Some(ai_conf) = &conf.ai { + let mut ai_info = Tree::new(); + + if ai_conf.gpu { + ai_info.add_str(format!("🟩 Use CUDA {}", "✔️".paint(Color::Green))); + } + + if let Some(models) = &ai_conf.models { + ai_info.add_str(format!("⬇️ Pull Models: {}", models.join(", "))); + } + + root_info.add_tree("🦙 Ollama", ai_info); + } + + println!("{}", root_info.render(0)); +} + +pub struct Tree { + elements: Vec, +} + +pub enum TreeEntry { + Tree(String, Tree), + Str(String), +} + +impl Tree { + pub fn new() -> Self { + Self { elements: vec![] } + } + + pub fn add_tree(&mut self, key: &str, item: Tree) { + self.elements.push(TreeEntry::Tree(key.to_string(), item)); + } + + pub fn add_str(&mut self, item: String) { + self.elements.push(TreeEntry::Str(item)); + } + + pub fn render(&self, depth: i32) -> String { + const DEPTH_INCREASE: i32 = 3; + + let mut ret = String::new(); + + let len = self.elements.len(); + + for (index, val) in self.elements.iter().enumerate() { + let padding = pad_with_bars(depth, DEPTH_INCREASE); + + let path_repr = if (index + 1) == len { + if matches!(val, TreeEntry::Tree(_, _)) { + "├─" + } else { + "└─" + } + } else { + "├─" + }; + + match val { + TreeEntry::Tree(key, tree) => { + if !tree.elements.is_empty() { + ret.push_str(&format!("{padding}{path_repr} {key}\n")); + ret.push_str(&tree.render(depth + DEPTH_INCREASE)) + } + } + TreeEntry::Str(str) => { + ret.push_str(&format!("{padding}{path_repr} {str}\n")); + } + } + } + + ret + } +} + +pub fn pad_with_bars(n: i32, depth_inc: i32) -> String { + let amount = n / depth_inc; + + let mut ret = String::new(); + + if amount >= 1 { + ret.push_str("│"); + } + + ret.push_str(&pad(n - amount)); + ret +} + +pub fn pad(n: i32) -> String { + let mut ret = String::new(); + + for _ in 0..n { + ret.push_str(" "); + } + + ret +}