fix: CDX via Backend-Proxy /cdx statt Browser-fetch

This commit is contained in:
2026-06-12 13:03:40 +02:00
parent 321f65a802
commit a415d3c691
2 changed files with 86 additions and 57 deletions

View File

@@ -13,52 +13,14 @@ function getDomain(url) {
catch { return url; }
}
// Wayback CDX direkt im Browser
async function searchWayback(targetUrl) {
const cdx = `https://web.archive.org/cdx/search/cdx?url=${encodeURIComponent(targetUrl)}&output=json&limit=1&fl=timestamp&filter=statuscode:200&fastLatest=true`;
let res;
try {
// Ohne custom headers minimaler Request damit CORS-Preflight entfällt
res = await fetch(cdx);
} catch (e) {
throw new Error('Netzwerkfehler: ' + e.message);
}
if (!res.ok) throw new Error(`CDX ${res.status}`);
const data = await res.json();
if (!Array.isArray(data) || data.length < 2 || !data[1]?.[0]) return null;
return `https://web.archive.org/web/${data[1][0]}/${targetUrl}`;
}
// Wayback Save + CDX-Polling
async function saveAndPollWayback(targetUrl, onStatus) {
// Save via no-cors (kein lesbarer Response, aber Archivierung wird getriggert)
try {
fetch(`https://web.archive.org/save/${targetUrl}`, {
method: 'GET',
mode: 'no-cors',
}).catch(() => {});
} catch {}
// CDX pollen: 8x alle 5s = max 40s
for (let i = 0; i < 8; i++) {
await new Promise(r => setTimeout(r, 5000));
onStatus(`Warte auf Snapshot... (${(i + 1) * 5}s)`);
try {
const url = await searchWayback(targetUrl);
if (url) return url;
} catch {}
}
return null;
}
export default function PaywallKiller() {
const [inputUrl, setInputUrl] = useState('');
const [status, setStatus] = useState('idle');
const [statusMsg, setStatusMsg] = useState('');
const [waybackUrl, setWaybackUrl] = useState('');
const [wasCreated, setWasCreated] = useState(false);
const [errMsg, setErrMsg] = useState('');
const [pdfLoading, setPdfLoading] = useState(false);
const [inputUrl, setInputUrl] = useState('');
const [status, setStatus] = useState('idle');
const [statusMsg, setStatusMsg] = useState('');
const [waybackUrl, setWaybackUrl] = useState('');
const [wasCreated, setWasCreated] = useState(false);
const [errMsg, setErrMsg] = useState('');
const [pdfLoading, setPdfLoading] = useState(false);
const inputRef = useRef(null);
function getTargetUrl() {
@@ -66,6 +28,31 @@ export default function PaywallKiller() {
return url.startsWith('http') ? url : 'https://' + url;
}
// CDX-Suche über Backend-Proxy (umgeht CORS/Origin-Problem der PWA)
async function searchWayback(targetUrl) {
const data = await api(`/tools/paywallkiller/cdx?url=${encodeURIComponent(targetUrl)}`, { method: 'GET' });
return data.found ? data.waybackUrl : null;
}
// Save via Browser (no-cors, fire-and-forget), dann CDX-Polling via Backend
async function saveAndPoll(targetUrl) {
// Save-Request abschießen (Browser, kein CORS-Response nötig)
try {
fetch(`https://web.archive.org/save/${targetUrl}`, { mode: 'no-cors' }).catch(() => {});
} catch {}
// CDX pollen via Backend-Proxy
for (let i = 0; i < 8; i++) {
await new Promise(r => setTimeout(r, 5000));
setStatusMsg(`Warte auf Snapshot... (${(i + 1) * 5}s)`);
try {
const url = await searchWayback(targetUrl);
if (url) return url;
} catch {}
}
return null;
}
async function handleCheck() {
if (!inputUrl.trim()) return;
const targetUrl = getTargetUrl();
@@ -76,18 +63,16 @@ export default function PaywallKiller() {
setWasCreated(false);
try {
// Schritt 1: vorhandenen Snapshot suchen
const existing = await searchWayback(targetUrl);
if (existing) {
setWaybackUrl(existing);
setStatus('done');
return;
}
// Schritt 2: Kein Snapshot → neu erstellen + pollen
// Kein Snapshot → archivieren
setStatus('saving');
setStatusMsg('Kein Snapshot gefunden archiviere jetzt...');
const newUrl = await saveAndPollWayback(targetUrl, msg => setStatusMsg(msg));
setStatusMsg('Kein Snapshot archiviere jetzt...');
const newUrl = await saveAndPoll(targetUrl);
if (newUrl) {
setWaybackUrl(newUrl);
setWasCreated(true);
@@ -185,7 +170,6 @@ export default function PaywallKiller() {
</div>
</div>
{/* Wayback-Ergebnis */}
{isDone && (
<div style={{ ...S.card, borderColor: `${C.success}55`, marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
@@ -209,19 +193,17 @@ export default function PaywallKiller() {
</div>
)}
{/* Kein Snapshot erstellt */}
{status === 'notfound' && (
<div style={{ ...S.card, borderColor: `${C.error}44`, marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span>📭</span>
<span style={{ color: C.error, fontFamily: 'monospace', fontSize: 12 }}>
Archivierung fehlgeschlagen {getDomain(inputUrl)} blockiert möglicherweise die Wayback Machine.
Kein Snapshot gefunden {getDomain(inputUrl)} blockiert möglicherweise die Wayback Machine.
</span>
</div>
</div>
)}
{/* archive.ph immer nach Suche anzeigen */}
{hasResult && archivePhUrl && (
<div style={{ ...S.card, borderColor: 'rgba(255,255,255,0.1)', marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
@@ -249,7 +231,7 @@ export default function PaywallKiller() {
<div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div>
{[
['1', 'URL einfügen (Long-Press → Einfügen)'],
['2', 'Wayback Machine wird direkt im Browser durchsucht'],
['2', 'Wayback Machine wird serverseitig durchsucht'],
['3', 'Falls kein Snapshot: Artikel wird automatisch archiviert'],
['4', 'Snapshot öffnen oder als PDF speichern'],
].map(([num, text]) => (