1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-07-21 18:55:36 +02:00

capture: close dialog on Archive; rich per-URL and batch toast notifications (#22)

* http: realistic UA; fall back to WebPage on probe failure

Replace bare 'archivr/0.1' user agent with a full Chrome 131 UA string
(archivr/0.1 token retained at the end) in both probe_url_kind and download.

More importantly, stop hard-failing when probe_url_kind returns an error.
Sites behind Cloudflare's managed JS challenge (e.g. Medium) return 403
before any header tuning can help — a plain HTTP client cannot pass the
challenge. Instead, log a warning and fall back to Source::WebPage so that
the SingleFile/Chromium path gets a chance; a real browser can solve the
challenge transparently.

* capture: close dialog on Archive; rich per-URL and batch toast notifications

UX changes:
- Pressing Archive closes the capture dialog immediately; jobs continue
  polling in the background (component stays mounted).
- Probe failures (e.g. Cloudflare JS challenge, HTTP 403) now fall back to
  Source::WebPage so SingleFile/Chromium can attempt the capture instead of
  hard-failing at the probe stage.

Toast notifications:
- Single URL: green 'Archived' on success, amber 'Archived with warnings'
  (with expandable detail) when uBlock/cookie-ext was skipped, red 'Capture
  failed' with expandable error text on failure. Per-item warning/error toasts
  include the locator so the user knows which URL was affected.
- Multi-URL batch: per-item failure and warning toasts still fire with
  locators; per-item success toasts are suppressed. Once all jobs settle a
  single summary toast fires: 'N archived', 'N archived (M with warnings)',
  'N archived (M with warnings), F failed', or 'N failed'. The summary Detail
  section lists the exact URLs that failed or warned, so the user retains that
  information after per-item toasts auto-dismiss.
- Batch summary color: green = all clean; amber = any warnings or failures
  present; red = all failed.
- handleIgnoreUblock now only removes per-item warning toasts (those with a
  locator) and persists the ignore flag; batch summary warnings (locator=null)
  are not swept.

ToastStack improvements:
- All three branches (success/warning/error) support a toast.headline field
  so batch summaries can set their own copy.
- Warning branch: Details button conditional on toast.text; Ignore button
  conditional on toast.locator (uBlock-specific, not shown on batch summaries).
- Locator display uses hostname/…/last-segment for URLs (preserves domain for
  context, tail for identity) and tail-truncation for shorthands; full locator
  in title attribute for hover.
- Icon colors: green for success (✓), amber for warning (⚠), red for error (✕).
This commit is contained in:
TheGeneralist 2026-07-11 15:29:10 +02:00 committed by GitHub
parent 2e8820a0da
commit 03390362c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 195 additions and 89 deletions

View file

@ -900,14 +900,12 @@ pub fn perform_capture(
Ok(downloader::http::UrlKind::Html) => source = Source::WebPage,
Ok(downloader::http::UrlKind::File) => {}
Err(e) => {
// Record a failed item using the pre-probe source_kind so
// failed_count increments and the item carries error_text.
let (probe_sk, probe_ek, _) = source_metadata(Source::Url);
let item = database::create_archive_run_item(
&conn, run.id, None, 0, locator, None, probe_sk, probe_ek,
)?;
let msg = format!("Failed to probe URL: {e}");
return Err(fail_run(&conn, &run, &item, &msg));
// Probe failed (e.g. Cloudflare JS challenge, 403, network
// issue). Fall back to treating the URL as an HTML page so
// that the SingleFile/Chromium path can try — a real browser
// can pass bot challenges that a plain HTTP client cannot.
eprintln!("warn: probe failed for {locator}, assuming HTML: {e}");
source = Source::WebPage;
}
}
}

View file

@ -20,7 +20,7 @@ pub enum UrlKind {
pub fn probe_url_kind(url: &str, cookies: &HashMap<String, String>) -> Result<UrlKind> {
let client = reqwest::blocking::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.user_agent("archivr/0.1")
.user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 archivr/0.1")
.build()
.context("failed to build HTTP client")?;
@ -86,7 +86,7 @@ pub fn probe_url_kind(url: &str, cookies: &HashMap<String, String>) -> Result<Ur
pub fn download(url: &str, store_path: &Path, timestamp: &str, cookies: &HashMap<String, String>) -> Result<(String, String, Option<String>)> {
let client = reqwest::blocking::Client::builder()
.redirect(reqwest::redirect::Policy::limited(10))
.user_agent("archivr/0.1")
.user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 archivr/0.1")
.build()
.context("failed to build HTTP client")?;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,8 +4,8 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Archivr</title>
<script type="module" crossorigin src="/assets/index-D6iX-c2T.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C0BIEdow.css">
<script type="module" crossorigin src="/assets/index-BbGz9qlG.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D0Nzcia0.css">
</head>
<body>
<div id="root"></div>