Upload-Share: Route registriert, Fehlversuche-Limit, PW kopieren, bessere Fehlerbehandlung

This commit is contained in:
2026-06-05 23:25:45 +02:00
parent 7e19a6580e
commit e7ebf2d831
5 changed files with 187 additions and 189 deletions

View File

@@ -169,6 +169,8 @@ function UploadShareManager({ toast }) {
const [form, setForm] = useState({ password:'', expires_hours:24, max_size_mb:10 });
const [busy, setBusy] = useState(false);
const [copied, setCopied] = useState(null);
const [lastPw, setLastPw] = useState(''); // für Passwort-Kopieren nach Erstellung
const [lastToken, setLastToken] = useState('');
const load = () => api('/upload-shares').then(d=>{ setShares(d.shares||[]); setFolder(d.folder); }).catch(()=>{});
useEffect(()=>{ load(); },[]);
@@ -182,7 +184,8 @@ function UploadShareManager({ toast }) {
if (!form.password.trim()) { toast('Passwort erforderlich','error'); return; }
setBusy(true);
try {
await api('/upload-shares', { body: form });
const r = await api('/upload-shares', { body: form });
setLastPw(form.password); setLastToken(r.token||'');
toast('Upload-Link erstellt ✓'); setShowCreate(false); setForm({password:'',expires_hours:24,max_size_mb:10}); load();
} catch(e) { toast(e.message,'error'); }
setBusy(false);
@@ -272,7 +275,7 @@ function UploadShareManager({ toast }) {
</div>}
</div>
<button onClick={create} disabled={busy} style={{...S.btn('#4ecdc4'),width:'100%',textAlign:'center',padding:'10px 0',opacity:busy?0.5:1}}>
{busy?'…':'🔗 Link erstellen & kopieren'}
{busy?'…':'🔗 Link erstellen'}
</button>
</div>
)}
@@ -293,9 +296,9 @@ function UploadShareManager({ toast }) {
<div style={{display:'flex',alignItems:'center',gap:10,flexWrap:'wrap'}}>
<div style={{flex:1,minWidth:0}}>
<div style={{display:'flex',alignItems:'center',gap:6,flexWrap:'wrap'}}>
<span style={{color:!s.is_active||expired?'#ff6b9d':expired?'#ffe66d':'#4ecdc4',fontFamily:'monospace',fontSize:11,
<span style={{color:!s.is_active?'#ff6b9d':expired?'#ffe66d':'#4ecdc4',fontFamily:'monospace',fontSize:11,
border:`1px solid currentColor`,borderRadius:4,padding:'1px 6px',flexShrink:0}}>
{!s.is_active?'Deaktiviert':expired?'Abgelaufen':'Aktiv'}
{!s.is_active?(s.deactivated_reason==='too_many_attempts'?'⚠ Gesperrt (Fehlversuche)':'Deaktiviert'):expired?'Abgelaufen':'Aktiv'}
</span>
<span style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:10}}>
bis {fmtDate(s.expires_at)} · max {s.max_size_mb} MB · {s.upload_count||0} Upload(s)
@@ -308,10 +311,16 @@ function UploadShareManager({ toast }) {
</div>
<div style={{display:'flex',gap:5,flexShrink:0}}>
{s.is_active && !expired && (
<>
<button onClick={()=>copyLink(s.token)}
style={{...S.btn(copied===s.token?'#4ecdc4':'#ffe66d',copied!==s.token),fontSize:10,padding:'4px 10px'}}>
{copied===s.token?'✓ Kopiert':'📋 Link'}
</button>
{lastToken===s.token && lastPw && (
<button onClick={()=>{ navigator.clipboard?.writeText(lastPw).then(()=>toast('Passwort kopiert ✓')).catch(()=>toast(lastPw)); }}
style={{...S.btn('#ffe66d',true),fontSize:10,padding:'4px 10px'}}>🔑 PW</button>
)}
</>
)}
{s.is_active && (
<button onClick={()=>deactivate(s.id)} style={{...S.btn('#ff6b9d',true),fontSize:10,padding:'4px 10px'}}></button>