Aurora-Print/Core/headless/mod.rs
2025-10-24 13:01:16 +02:00

137 lines
No EOL
4.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::{path::Path, time::Duration};
use futures::StreamExt;
use chromiumoxide::browser::{Browser, BrowserConfig};
use chromiumoxide::cdp::browser_protocol::page::NavigateParams;
use chromiumoxide::cdp::browser_protocol::input::InsertTextParams;
use crate::state::{SharedState, HeadlessPhase};
use crate::config::read_config;
pub async fn start(state: SharedState) -> Result<(), Box<dyn std::error::Error>> {
{
let mut s = state.lock().unwrap();
s.headless = HeadlessPhase::Starting;
}
// Hier später: chromiumoxide Browser-Launch + Healthcheck
// Für jetzt: kurze simulierte Init-Zeit
tokio::time::sleep(std::time::Duration::from_millis(400)).await;
{
let mut s = state.lock().unwrap();
s.headless = HeadlessPhase::Ready;
}
Ok(())
}
pub struct HeadlessManager {
browser: chromiumoxide::Browser,
_handler_task: tokio::task::JoinHandle<()>,
}
pub struct PageHandle {
page: chromiumoxide::Page,
}
impl HeadlessManager {
/// Startet Chromium (für Tests ohne Headless: wir filtern --headless raus)
pub async fn new(chrome_exe: &Path, extra_args: &[String]) -> Result<Self, Box<dyn std::error::Error>> {
let mut cfg = BrowserConfig::builder();
cfg = cfg.chrome_executable(chrome_exe); // v0.7 API
for a in extra_args {
// fürs Testen: Headless-Flag unterdrücken, damit ein Fenster sichtbar ist
if a.starts_with("--headless") { continue; }
cfg = cfg.arg(a);
}
let (browser, mut handler) = Browser::launch(cfg.build()?).await?;
// Event-Handler-Loop muss laufen
let handler_task = tokio::spawn(async move {
while let Some(_evt) = handler.next().await {
// hier ggf. Logging
}
});
Ok(Self { browser, _handler_task: handler_task })
}
pub async fn new_page(&self) -> Result<PageHandle, Box<dyn std::error::Error>> {
let page = self.browser.new_page("about:blank").await?;
Ok(PageHandle { page })
}
}
impl PageHandle {
pub async fn goto(&self, url: &str) -> Result<(), Box<dyn std::error::Error>> {
self.page.goto(NavigateParams::builder().url(url).build()?).await?;
// manche UIs brauchen einen Moment
self.page.wait_for_navigation().await.ok();
Ok(())
}
/// Wartet auf einen CSS-Selector, true wenn gefunden, false bei Timeout
pub async fn wait_for_selector(&self, selector: &str, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
match tokio::time::timeout(
Duration::from_millis(timeout_ms),
self.page.find_element(selector),
).await {
Ok(Ok(_elem)) => Ok(true),
_ => Ok(false),
}
}
/// Liest innerText eines Elements, None falls nicht gefunden/Fehler
pub async fn get_text(&self, selector: &str) -> Result<Option<String>, Box<dyn std::error::Error>> {
let elem = match self.page.find_element(selector).await {
Ok(e) => e,
Err(_) => return Ok(None),
};
match elem.inner_text().await {
Ok(opt) => Ok(opt), // opt ist bereits Option<String>
Err(_) => Ok(None),
}
}
/// Tippt Text in ein (vorher angeklicktes) Eingabefeld
pub async fn type_text(&self, selector: &str, text: &str) -> Result<(), Box<dyn std::error::Error>> {
let elem = self.page.find_element(selector).await?;
elem.click().await?;
// Fokus ist im Feld jetzt Text am Stück einfügen
self.page.execute(InsertTextParams::new(text.to_string())).await?;
Ok(())
}
pub async fn click(&self, selector: &str) -> Result<(), Box<dyn std::error::Error>> {
let elem = self.page.find_element(selector).await?;
elem.click().await?;
Ok(())
}
}
pub async fn run_test(ip: &str) -> Result<(), Box<dyn std::error::Error>> {
println!("[Headless] Starte Test für {ip}");
// config lesen, um Pfad zu Chrome zu bekommen
let exe_dir = std::env::current_exe()?.parent().unwrap().to_path_buf();
let cfg = read_config(&exe_dir)?;
let chrome_exe = Path::new(&cfg.chrome.path);
let manager = HeadlessManager::new(chrome_exe, &cfg.chrome.args).await?;
let page = manager.new_page().await?;
// Zielseite öffnen
let url = format!("https://{ip}/");
page.goto(&url).await?;
// kurz warten & Text auslesen
let _ = page.wait_for_selector("h1", 5000).await?;
if let Some(h1) = page.get_text("h1").await? {
println!("[Headless] H1: {}", h1);
} else {
println!("[Headless] Keine H1 gefunden");
}
Ok(())
}