diff --git a/backend/src/tools/paywallkiller/routes.js b/backend/src/tools/paywallkiller/routes.js index cc2c905..e38b3ff 100644 --- a/backend/src/tools/paywallkiller/routes.js +++ b/backend/src/tools/paywallkiller/routes.js @@ -229,4 +229,35 @@ router.post('/pdf', authenticate, async (req, res) => { } }); + +// ── GET /api/tools/paywallkiller/wayback?url=... ────────────────────────── +// Proxyt die Wayback CDX API – umgeht CORS/CSP Probleme im Browser +router.get('/wayback', authenticate, async (req, res) => { + const { url } = req.query; + if (!url || typeof url !== 'string') return res.status(400).json({ error: 'url Parameter fehlt' }); + + let targetUrl = url.trim(); + if (!targetUrl.startsWith('http')) targetUrl = 'https://' + targetUrl; + + const cdxUrl = `https://web.archive.org/cdx/search/cdx?url=${encodeURIComponent(targetUrl)}&output=json&limit=1&fl=timestamp&filter=statuscode:200&fastLatest=true`; + + try { + const result = await fetchPage(cdxUrl); + if (result.statusCode !== 200) { + return res.status(502).json({ error: `Wayback API Fehler: ${result.statusCode}` }); + } + let data; + try { data = JSON.parse(result.body); } catch { return res.status(502).json({ error: 'Ungültige Wayback-Antwort' }); } + + // data = [["timestamp"],["20260608201434"]] oder [] wenn nichts gefunden + if (!Array.isArray(data) || data.length < 2 || !data[1]?.[0]) { + return res.json({ found: false }); + } + const timestamp = data[1][0]; + return res.json({ found: true, waybackUrl: `https://web.archive.org/web/${timestamp}/${targetUrl}` }); + } catch (err) { + return res.status(500).json({ error: 'Fehler: ' + err.message }); + } +}); + module.exports = router; diff --git a/frontend/src/tools/paywallkiller.jsx b/frontend/src/tools/paywallkiller.jsx index 6208c9a..cd1fac7 100644 --- a/frontend/src/tools/paywallkiller.jsx +++ b/frontend/src/tools/paywallkiller.jsx @@ -1,5 +1,5 @@ import { useState, useRef } from 'react'; -import { S } from '../lib.js'; +import { S, api } from '../lib.js'; const C = { accent: '#f59e0b', @@ -13,18 +13,6 @@ function getDomain(url) { catch { return url; } } -// Wayback CDX API – direkt im Browser (CORS: *) -async function checkWayback(targetUrl) { - 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(12000) }); - if (!res.ok) throw new Error(`CDX API Fehler: ${res.status}`); - const data = await res.json(); - if (!Array.isArray(data) || data.length < 2) return null; - const timestamp = data[1]?.[0]; - if (!timestamp) return null; - return `https://web.archive.org/web/${timestamp}/${targetUrl}`; -} - export default function PaywallKiller() { const [inputUrl, setInputUrl] = useState(''); const [status, setStatus] = useState('idle'); @@ -41,9 +29,8 @@ export default function PaywallKiller() { setErrMsg(''); setWaybackUrl(''); try { - const wb = await checkWayback(targetUrl); - setWaybackUrl(wb || ''); - // Immer "found" zeigen – Wayback-Treffer oder nicht, archive.ph Link immer anbieten + const data = await api(`/tools/paywallkiller/wayback?url=${encodeURIComponent(targetUrl)}`, { method: 'GET' }); + setWaybackUrl(data.found ? data.waybackUrl : ''); setStatus('done'); } catch (err) { setErrMsg(err.message || 'Fehler beim Suchen'); @@ -94,19 +81,6 @@ export default function PaywallKiller() { setTimeout(() => inputRef.current?.focus(), 50); } - function handlePaste() { - // Fokus ins Input setzen und nativen Paste auslösen – kein Permission-Dialog - const input = inputRef.current; - if (!input) return; - input.focus(); - // execCommand('paste') funktioniert auf Android ohne Clipboard-Permission - const ok = document.execCommand('paste'); - if (!ok) { - // Fallback: nur fokussieren, User kann selbst einfügen - input.select(); - } - } - const isChecking = status === 'checking'; const isDone = status === 'done'; const hasWayback = isDone && !!waybackUrl; @@ -131,24 +105,18 @@ export default function PaywallKiller() { {/* Eingabe */}
ARTIKEL-URL
-
- setInputUrl(e.target.value)} - onKeyDown={e => e.key === 'Enter' && !isChecking && handleCheck()} - placeholder="https://www.zeitung.de/artikel/..." - style={{ ...S.inp, flex: 1 }} - /> - -
+ setInputUrl(e.target.value)} + onKeyDown={e => e.key === 'Enter' && !isChecking && handleCheck()} + placeholder="URL hier einfügen (Long-Press → Einfügen)" + style={{ ...S.inp, width: '100%', boxSizing: 'border-box', marginBottom: 10 }} + />
{(isDone || status === 'error') && (
- {/* archive.ph – nur Öffnen, kein PDF (CAPTCHA-Schutz) */} + {/* archive.ph */}
📦 @@ -212,12 +175,8 @@ export default function PaywallKiller() {
- 🌐 Öffnen - - - Kein PDF — CAPTCHA-geschützt - + style={{ ...S.btn(C.muted), textDecoration: 'none', fontSize: 12 }}>🌐 Öffnen + Kein PDF – CAPTCHA-geschützt
@@ -238,14 +197,14 @@ export default function PaywallKiller() {
SO FUNKTIONIERT ES
{[ - ['1', 'URL des gesperrten Artikels einfügen'], - ['2', 'Wayback Machine wird durchsucht (web.archive.org)'], + ['1', 'URL des gesperrten Artikels ins Textfeld einfügen (Long-Press → Einfügen)'], + ['2', 'Wayback Machine (web.archive.org) wird serverseitig durchsucht'], ['3', 'Snapshot öffnen oder als PDF speichern'], - ['4', 'Alternativ: archive.ph im Browser öffnen'], + ['4', 'Alternativ: archive.ph direkt im Browser öffnen'], ].map(([num, text]) => ( -
- {num} - {text} +
+ {num} + {text}
))}