fix: Wayback-Suche via Backend-Proxy, Clipboard-Button entfernt
This commit is contained in:
@@ -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 */}
|
||||
<div style={{ ...S.card, marginBottom: 16 }}>
|
||||
<div style={{ ...S.head, marginBottom: 10 }}>ARTIKEL-URL</div>
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={inputUrl}
|
||||
onChange={e => setInputUrl(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && !isChecking && handleCheck()}
|
||||
placeholder="https://www.zeitung.de/artikel/..."
|
||||
style={{ ...S.inp, flex: 1 }}
|
||||
/>
|
||||
<button onClick={handlePaste} title="Aus Zwischenablage"
|
||||
style={{ background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 6, padding: '0 12px', color: '#fff', cursor: 'pointer', fontSize: 14, flexShrink: 0 }}>
|
||||
📋
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={inputUrl}
|
||||
onChange={e => 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 }}
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<button onClick={handleCheck} disabled={isChecking || !inputUrl.trim()}
|
||||
style={{ ...S.btn(C.accent), flex: 1, opacity: (isChecking || !inputUrl.trim()) ? 0.45 : 1, cursor: (isChecking || !inputUrl.trim()) ? 'default' : 'pointer' }}>
|
||||
{isChecking ? '⏳ Suche Archiv...' : '🔍 Archiv suchen'}
|
||||
{isChecking ? '⏳ Suche in Wayback Machine...' : '🔍 Archiv suchen'}
|
||||
</button>
|
||||
{(isDone || status === 'error') && (
|
||||
<button onClick={handleReset}
|
||||
@@ -166,9 +134,7 @@ export default function PaywallKiller() {
|
||||
<div style={{ ...S.card, borderColor: hasWayback ? `${C.success}55` : 'rgba(255,255,255,0.1)', marginBottom: 12 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: hasWayback ? 10 : 0 }}>
|
||||
<span>🏛</span>
|
||||
<span style={{ color: hasWayback ? C.success : C.muted, fontFamily: 'monospace', fontSize: 12 }}>
|
||||
Wayback Machine
|
||||
</span>
|
||||
<span style={{ color: hasWayback ? C.success : C.muted, fontFamily: 'monospace', fontSize: 12 }}>Wayback Machine</span>
|
||||
<span style={{
|
||||
background: hasWayback ? `${C.success}20` : 'rgba(255,255,255,0.06)',
|
||||
border: `1px solid ${hasWayback ? C.success + '44' : 'rgba(255,255,255,0.12)'}`,
|
||||
@@ -178,7 +144,6 @@ export default function PaywallKiller() {
|
||||
{hasWayback ? 'Snapshot gefunden' : 'kein Snapshot'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{hasWayback && (
|
||||
<>
|
||||
<div style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 6, padding: '7px 10px', marginBottom: 10, wordBreak: 'break-all' }}>
|
||||
@@ -187,9 +152,7 @@ export default function PaywallKiller() {
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||
<a href={waybackUrl} target="_blank" rel="noreferrer"
|
||||
style={{ ...S.btn(C.success), textDecoration: 'none', fontSize: 12 }}>
|
||||
🌐 Öffnen
|
||||
</a>
|
||||
style={{ ...S.btn(C.success), textDecoration: 'none', fontSize: 12 }}>🌐 Öffnen</a>
|
||||
<button onClick={handlePdf} disabled={pdfLoading}
|
||||
style={{ ...S.btn(C.accent), opacity: pdfLoading ? 0.5 : 1, cursor: pdfLoading ? 'default' : 'pointer', fontSize: 12 }}>
|
||||
{pdfLoading ? '⏳ Erstelle PDF...' : '⬇ Als PDF'}
|
||||
@@ -204,7 +167,7 @@ export default function PaywallKiller() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* archive.ph – nur Öffnen, kein PDF (CAPTCHA-Schutz) */}
|
||||
{/* archive.ph */}
|
||||
<div style={{ ...S.card, borderColor: 'rgba(255,255,255,0.1)', marginBottom: 12 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
||||
<span>📦</span>
|
||||
@@ -212,12 +175,8 @@ export default function PaywallKiller() {
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||
<a href={archivePhUrl} target="_blank" rel="noreferrer"
|
||||
style={{ ...S.btn(C.muted), textDecoration: 'none', fontSize: 12 }}>
|
||||
🌐 Öffnen
|
||||
</a>
|
||||
<span style={{ color: C.muted, fontSize: 10, fontFamily: 'monospace' }}>
|
||||
Kein PDF — CAPTCHA-geschützt
|
||||
</span>
|
||||
style={{ ...S.btn(C.muted), textDecoration: 'none', fontSize: 12 }}>🌐 Öffnen</a>
|
||||
<span style={{ color: C.muted, fontSize: 10, fontFamily: 'monospace' }}>Kein PDF – CAPTCHA-geschützt</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -238,14 +197,14 @@ export default function PaywallKiller() {
|
||||
<div style={{ ...S.card }}>
|
||||
<div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div>
|
||||
{[
|
||||
['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]) => (
|
||||
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'center', marginBottom: 8 }}>
|
||||
<span style={{ background: `${C.accent}20`, border: `1px solid ${C.accent}44`, borderRadius: '50%', width: 20, height: 20, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, color: C.accent, fontFamily: 'monospace' }}>{num}</span>
|
||||
<span style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace' }}>{text}</span>
|
||||
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 8 }}>
|
||||
<span style={{ background: `${C.accent}20`, border: `1px solid ${C.accent}44`, borderRadius: '50%', width: 20, height: 20, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, color: C.accent, fontFamily: 'monospace', marginTop: 2 }}>{num}</span>
|
||||
<span style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', lineHeight: 1.6 }}>{text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user