Added Core Features chroium, config,console_ui,headless,webserver,states
This commit is contained in:
parent
4c6c6d572c
commit
c3c60efb81
13 changed files with 688 additions and 0 deletions
86
Core/chromium/download.rs
Normal file
86
Core/chromium/download.rs
Normal 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
78
Core/chromium/mod.rs
Normal 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
42
Core/chromium/utils.rs
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue