diff --git a/backend/src/tools/paywallkiller/routes.js b/backend/src/tools/paywallkiller/routes.js index f430cf7..cc2c905 100644 --- a/backend/src/tools/paywallkiller/routes.js +++ b/backend/src/tools/paywallkiller/routes.js @@ -166,8 +166,8 @@ router.post('/check', authenticate, async (req, res) => { router.post('/pdf', authenticate, async (req, res) => { const { archiveUrl } = req.body; if (!archiveUrl || typeof archiveUrl !== 'string') return res.status(400).json({ error: 'archiveUrl fehlt' }); - if (!/^https?:\/\/(archive\.(is|ph|today|fo|li|vn|md|gg)|web\.archive\.org)\//.test(archiveUrl)) { - return res.status(400).json({ error: 'Ungültige Archiv-URL' }); + if (!archiveUrl.startsWith('https://web.archive.org/')) { + return res.status(400).json({ error: 'PDF nur für Wayback Machine URLs (web.archive.org) verfügbar' }); } let browser; diff --git a/frontend/src/tools/paywallkiller.jsx b/frontend/src/tools/paywallkiller.jsx index e5d9176..b13e223 100644 --- a/frontend/src/tools/paywallkiller.jsx +++ b/frontend/src/tools/paywallkiller.jsx @@ -13,14 +13,12 @@ function getDomain(url) { catch { return url; } } -// ── Wayback Machine CDX API – direkt im Browser (CORS: *) ───────────────── +// Wayback CDX API – direkt im Browser (CORS: *) async function checkWayback(targetUrl) { - // Neuesten Snapshot suchen (limit=1, sortiert newest-first via fl+limit) const cdxUrl = `https://web.archive.org/cdx/search/cdx?url=${encodeURIComponent(targetUrl)}&output=json&limit=1&fl=timestamp&filter=statuscode:200&fastLatest=true`; - const res = await fetch(cdxUrl, { signal: AbortSignal.timeout(10000) }); - if (!res.ok) return null; + const res = await fetch(cdxUrl, { signal: AbortSignal.timeout(12000) }); + if (!res.ok) throw new Error(`CDX API Fehler: ${res.status}`); const data = await res.json(); - // data = [["timestamp"],["20260608201434"]] oder [] wenn nichts gefunden if (!Array.isArray(data) || data.length < 2) return null; const timestamp = data[1]?.[0]; if (!timestamp) return null; @@ -31,48 +29,32 @@ export default function PaywallKiller() { const [inputUrl, setInputUrl] = useState(''); const [status, setStatus] = useState('idle'); const [waybackUrl, setWaybackUrl] = useState(''); - const [archiveUrl, setArchiveUrl] = useState(''); const [errMsg, setErrMsg] = useState(''); const [pdfLoading, setPdfLoading] = useState(false); - const [pdfTarget, setPdfTarget] = useState(''); const inputRef = useRef(null); async function handleCheck() { const url = inputUrl.trim(); if (!url) return; - let targetUrl = url.startsWith('http') ? url : 'https://' + url; - + const targetUrl = url.startsWith('http') ? url : 'https://' + url; setStatus('checking'); setErrMsg(''); setWaybackUrl(''); - setArchiveUrl(''); - try { - // archive.ph URL immer konstruieren (ohne Prüfung – direkt nutzbar) - const archivePh = `https://archive.ph/newest/${targetUrl}`; - - // Wayback CDX parallel prüfen - let wb = null; - try { wb = await checkWayback(targetUrl); } catch { /* ignorieren */ } - + const wb = await checkWayback(targetUrl); setWaybackUrl(wb || ''); - setArchiveUrl(archivePh); - - if (wb) { - setStatus('found_both'); - } else { - // Auch ohne Wayback-Treffer archive.ph anbieten - setStatus('found_archive'); - } + // Immer "found" zeigen – Wayback-Treffer oder nicht, archive.ph Link immer anbieten + setStatus('done'); } catch (err) { setErrMsg(err.message || 'Fehler beim Suchen'); setStatus('error'); } } - async function handlePdf(url) { - setPdfTarget(url); + async function handlePdf() { + if (!waybackUrl) return; setPdfLoading(true); + setErrMsg(''); try { const token = localStorage.getItem('sk_token'); const res = await fetch('/api/tools/paywallkiller/pdf', { @@ -81,7 +63,7 @@ export default function PaywallKiller() { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), }, - body: JSON.stringify({ archiveUrl: url }), + body: JSON.stringify({ archiveUrl: waybackUrl }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); @@ -100,7 +82,6 @@ export default function PaywallKiller() { setErrMsg(err.message || 'PDF konnte nicht erstellt werden'); } finally { setPdfLoading(false); - setPdfTarget(''); } } @@ -108,7 +89,6 @@ export default function PaywallKiller() { setInputUrl(''); setStatus('idle'); setWaybackUrl(''); - setArchiveUrl(''); setErrMsg(''); setPdfLoading(false); setTimeout(() => inputRef.current?.focus(), 50); @@ -122,24 +102,16 @@ export default function PaywallKiller() { } const isChecking = status === 'checking'; - const hasResult = status.startsWith('found') || status === 'error'; - const hasWayback = status === 'found_both' && waybackUrl; - const hasArchive = (status === 'found_both' || status === 'found_archive') && archiveUrl; - - // PDF-Button für eine bestimmte URL - function PdfBtn({ url }) { - const loading = pdfLoading && pdfTarget === url; - return ( - - ); - } + const isDone = status === 'done'; + const hasWayback = isDone && !!waybackUrl; + const archivePhUrl = isDone + ? `https://archive.ph/newest/${(inputUrl.startsWith('http') ? inputUrl : 'https://' + inputUrl).trim()}` + : ''; return (