Added Core Features chroium, config,console_ui,headless,webserver,states

This commit is contained in:
Joey Pillunat 2025-10-24 11:52:34 +02:00
parent 4c6c6d572c
commit c3c60efb81
13 changed files with 688 additions and 0 deletions

86
Core/chromium/download.rs Normal file
View file

@ -0,0 +1,86 @@
use std::{fs, io, path::{Path, PathBuf}};
use reqwest::blocking::Client;
use super::utils::extract_zip; // <-- statt util
#[cfg(target_os = "windows")]
const PLATFORM_DIR: &str = "Win_x64";
#[cfg(target_os = "linux")]
const PLATFORM_DIR: &str = "Linux_x64";
#[cfg(target_os = "macos")]
const PLATFORM_DIR: &str = "Mac";
fn zip_name() -> &'static str {
#[cfg(target_os = "windows")] { "chrome-win.zip" }
#[cfg(target_os = "linux")] { "chrome-linux.zip" }
#[cfg(target_os = "macos")] { "chrome-mac.zip" }
}
fn bin_relative_path() -> &'static str {
#[cfg(target_os = "windows")] { "chrome-win/chrome.exe" }
#[cfg(target_os = "linux")] { "chrome-linux/chrome" }
#[cfg(target_os = "macos")] { "chrome-mac/Chromium.app/Contents/MacOS/Chromium" }
}
fn fetch_latest_revision(client: &Client) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let url = format!(
"https://storage.googleapis.com/chromium-browser-snapshots/{}/LAST_CHANGE",
PLATFORM_DIR
);
let txt = client.get(url).send()?.error_for_status()?.text()?;
Ok(txt.trim().to_string())
}
pub fn find_latest_local(cache_dir: &Path) -> io::Result<Option<PathBuf>> {
let mut best: Option<(u64, PathBuf)> = None;
if !cache_dir.exists() { return Ok(None); }
for entry in fs::read_dir(cache_dir)? {
let entry = entry?;
if !entry.file_type()?.is_dir() { continue; }
let name = entry.file_name().to_string_lossy().to_string();
if let Ok(n) = name.parse::<u64>() {
let bin = entry.path().join(bin_relative_path());
if bin.exists() {
if let Some((best_n, _)) = best {
if n > best_n { best = Some((n, bin)); }
} else {
best = Some((n, bin));
}
}
}
}
Ok(best.map(|(_, p)| p))
}
fn download_to(client: &Client, url: &str, dest: &Path) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut resp = client.get(url).send()?.error_for_status()?;
let mut file = fs::File::create(dest)?;
io::copy(&mut resp, &mut file)?;
Ok(())
}
pub fn get_or_install_latest(cache_dir: &Path) -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>>
{
fs::create_dir_all(cache_dir)?;
let client = Client::builder().build()?;
let rev = fetch_latest_revision(&client)?;
let rev_dir = cache_dir.join(&rev);
let bin_path = rev_dir.join(bin_relative_path());
if bin_path.exists() {
return Ok(bin_path);
}
let url = format!(
"https://storage.googleapis.com/chromium-browser-snapshots/{}/{}/{}",
PLATFORM_DIR, rev, zip_name()
);
let zip_path = cache_dir.join(format!("chromium-{}.zip", rev));
if !zip_path.exists() {
download_to(&client, &url, &zip_path)?;
}
extract_zip(&zip_path, &rev_dir)?;
Ok(bin_path)
}

78
Core/chromium/mod.rs Normal file
View file

@ -0,0 +1,78 @@
use std::{path::{Path, PathBuf}};
use crate::config::{Config, save_config, resolve_path};
use crate::state::{SharedState, ChromiumPhase};
mod download;
pub mod utils;
pub const CACHE_DIR: &str = "runtime/chrome-cache";
pub async fn start_check(cfg: &mut Config, exe_dir: &Path, state: SharedState)
-> Result<PathBuf, Box<dyn std::error::Error>>
{
// Status: Checking
{
let mut s = state.lock().unwrap();
s.chromium = ChromiumPhase::Checking;
}
// ensure_chromium kann Download triggern (wir geben state weiter, um „Downloading“ zu setzen)
match ensure_chromium(cfg, exe_dir, state.clone()).await {
Ok(path) => {
let mut s = state.lock().unwrap();
s.chromium = ChromiumPhase::Ready;
Ok(path)
}
Err(e) => {
let mut s = state.lock().unwrap();
s.chromium = ChromiumPhase::Error(e.to_string());
Err(e)
}
}
}
async fn ensure_chromium(cfg: &mut Config, exe_dir: &Path, state: SharedState)
-> Result<PathBuf, Box<dyn std::error::Error>>
{
// 1) Pfad aus Config
if !cfg.chrome.path.is_empty() {
let p = resolve_path(exe_dir, &cfg.chrome.path);
if p.exists() {
return Ok(p);
}
}
// 2) Lokaler Cache?
if let Some(p) = find_existing_binary(exe_dir) {
cfg.chrome.path = p.to_string_lossy().to_string();
let _ = save_config(exe_dir, cfg);
return Ok(p);
}
// 3) Download erforderlich -> Status: Downloading
{
let mut s = state.lock().unwrap();
s.chromium = ChromiumPhase::Downloading;
}
let cache_dir = exe_dir.join(CACHE_DIR);
let bin_path_result = tokio::task::spawn_blocking(move || {
download::get_or_install_latest(&cache_dir)
}).await;
let bin_path = match bin_path_result {
Ok(Ok(path)) => path,
Ok(Err(e)) => return Err(e as Box<dyn std::error::Error>),
Err(e) => return Err(Box::new(e)),
};
cfg.chrome.path = bin_path.to_string_lossy().to_string();
let _ = save_config(exe_dir, cfg);
Ok(bin_path)
}
fn find_existing_binary(exe_dir: &Path) -> Option<PathBuf> {
let cache = exe_dir.join(CACHE_DIR);
download::find_latest_local(&cache).ok().flatten()
}

42
Core/chromium/utils.rs Normal file
View file

@ -0,0 +1,42 @@
use std::{fs, io::{self}, path::{Path, PathBuf}};
use zip::read::ZipArchive;
pub fn extract_zip(src: &Path, dest: &Path) -> io::Result<()> {
let file = fs::File::open(src)?;
let mut archive = ZipArchive::new(file)?;
fs::create_dir_all(dest)?;
for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
let outpath = dest.join(sanitize_path(entry.name()));
if entry.is_dir() {
fs::create_dir_all(&outpath)?;
} else {
if let Some(p) = outpath.parent() { fs::create_dir_all(p)?; }
let mut outfile = fs::File::create(&outpath)?;
io::copy(&mut entry, &mut outfile)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = entry.unix_mode() {
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?;
}
}
}
}
Ok(())
}
fn sanitize_path(name: &str) -> PathBuf {
let path = Path::new(name);
let mut clean = PathBuf::new();
for comp in path.components() {
use std::path::Component::*;
match comp {
Normal(c) => clean.push(c),
CurDir | ParentDir | RootDir | Prefix(_) => { /* skip */ }
}
}
clean
}