feat: Logs zeigen jetzt auch Besuche oeffentlicher Links (IP+Ort), Dropdown-Filter ergaenzt, Logs erfassen jetzt auch Upload-Freigabe und Datei-Teilen Besuche, Filter-Dropdown erweitert
This commit is contained in:
@@ -3189,14 +3189,28 @@ function WebDavSettings({ toast }) {
|
||||
const PRIORITY_LABELS = { '-2':'Silent', '-1':'Leise', '0':'Normal', '1':'Hoch', '2':'Emergency' };
|
||||
const PRIORITY_COLORS = { '-2':'#888888', '-1':'#4ecdc4', '0':'rgba(255,255,255,0.6)', '1':'#ffe66d', '2':'#ff6b9d' };
|
||||
|
||||
const LOG_TYPE_OPTIONS = [
|
||||
['alle', 'Alles'],
|
||||
['pushover', 'Pushover-Nachrichten'],
|
||||
['koepi_share', 'Öffentlicher Prospektlink-Zugriff'],
|
||||
['upload_share', 'Öffentlicher Upload-Link-Zugriff'],
|
||||
['file_share', 'Öffentlicher Datei-Teilen-Link-Zugriff'],
|
||||
];
|
||||
const PUBLIC_LINK_LABELS = {
|
||||
koepi_share: '🔗 Prospektlink besucht',
|
||||
upload_share: '📤 Upload-Link besucht',
|
||||
file_share: '📁 Datei-Teilen-Link besucht',
|
||||
};
|
||||
|
||||
function PushLogs({ toast }) {
|
||||
const [logs, setLogs] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filter, setFilter] = useState('');
|
||||
const [typeFilter, setTypeFilter] = useState('alle');
|
||||
|
||||
const load = () => {
|
||||
setLoading(true);
|
||||
api('/admin/push-logs?limit=300').then(setLogs).catch(e=>toast(e.message,'error')).finally(()=>setLoading(false));
|
||||
api('/admin/logs?limit=300').then(setLogs).catch(e=>toast(e.message,'error')).finally(()=>setLoading(false));
|
||||
};
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
@@ -3207,19 +3221,33 @@ function PushLogs({ toast }) {
|
||||
return d.toLocaleString('de-DE', { day:'2-digit', month:'2-digit', year:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit' });
|
||||
};
|
||||
|
||||
const byType = typeFilter === 'alle' ? logs
|
||||
: typeFilter === 'pushover' ? logs.filter(l => l.logType === 'pushover')
|
||||
: logs.filter(l => l.logType === 'public_access' && l.linkType === typeFilter);
|
||||
const filtered = filter.trim()
|
||||
? logs.filter(l => (l.username||'').toLowerCase().includes(filter.toLowerCase())
|
||||
|| (l.title||'').toLowerCase().includes(filter.toLowerCase())
|
||||
|| (l.message||'').toLowerCase().includes(filter.toLowerCase())
|
||||
|| (l.source||'').toLowerCase().includes(filter.toLowerCase()))
|
||||
: logs;
|
||||
? byType.filter(l => {
|
||||
const q = filter.toLowerCase();
|
||||
if (l.logType === 'pushover') {
|
||||
return (l.username||'').toLowerCase().includes(q) || (l.title||'').toLowerCase().includes(q)
|
||||
|| (l.message||'').toLowerCase().includes(q) || (l.source||'').toLowerCase().includes(q);
|
||||
}
|
||||
return (l.path||'').toLowerCase().includes(q) || (l.ip||'').toLowerCase().includes(q)
|
||||
|| (l.location||'').toLowerCase().includes(q) || (l.linkType||'').toLowerCase().includes(q);
|
||||
})
|
||||
: byType;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Sec title={`PUSHOVER-VERLAUF (${filtered.length})`}>
|
||||
<Sec title={`LOGS (${filtered.length})`}>
|
||||
<div style={{ display:'flex', gap:8, marginBottom:8, flexWrap:'wrap' }}>
|
||||
<select value={typeFilter} onChange={e=>setTypeFilter(e.target.value)}
|
||||
style={{ ...S.inp, flex:'1 1 200px' }}>
|
||||
{LOG_TYPE_OPTIONS.map(([id,label]) => <option key={id} value={id}>{label}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div style={{ display:'flex', gap:8, marginBottom:12 }}>
|
||||
<input value={filter} onChange={e=>setFilter(e.target.value)}
|
||||
placeholder="Filtern nach Benutzer, Text, Quelle…"
|
||||
placeholder="Filtern nach Benutzer, Text, IP, Ort…"
|
||||
style={{ ...S.inp, flex:1 }} />
|
||||
<button onClick={load} style={{ ...S.btn('#4ecdc4'), flexShrink:0 }}>↻</button>
|
||||
</div>
|
||||
@@ -3230,7 +3258,32 @@ function PushLogs({ toast }) {
|
||||
<div style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:12 }}>Keine Einträge.</div>
|
||||
) : (
|
||||
<div style={{ display:'flex', flexDirection:'column', gap:6, maxHeight:'65vh', overflowY:'auto' }}>
|
||||
{filtered.map(l => (
|
||||
{filtered.map(l => l.logType === 'public_access' ? (
|
||||
<div key={l.id} style={{
|
||||
border:'1px solid rgba(78,205,196,0.2)', borderRadius:8, padding:'8px 10px',
|
||||
background:'rgba(78,205,196,0.04)',
|
||||
}}>
|
||||
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:8, marginBottom:3, flexWrap:'wrap' }}>
|
||||
<span style={{ color:'#4ecdc4', fontFamily:"'Space Mono',monospace", fontSize:11, fontWeight:700 }}>
|
||||
{PUBLIC_LINK_LABELS[l.linkType] || '🔗 Öffentlicher Link besucht'}
|
||||
</span>
|
||||
<span style={{ color:'rgba(255,255,255,0.35)', fontFamily:'monospace', fontSize:10 }}>
|
||||
{fmtDate(l.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ color:'rgba(255,255,255,0.85)', fontFamily:'monospace', fontSize:12, marginBottom:4 }}>
|
||||
{l.path}
|
||||
</div>
|
||||
<div style={{ display:'flex', gap:12, flexWrap:'wrap' }}>
|
||||
<span style={{ color:'rgba(255,255,255,0.5)', fontFamily:'monospace', fontSize:10 }}>
|
||||
IP: {l.ip || 'unbekannt'}
|
||||
</span>
|
||||
<span style={{ color:'rgba(255,255,255,0.5)', fontFamily:'monospace', fontSize:10 }}>
|
||||
📍 {l.location || 'unbekannt'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div key={l.id} style={{
|
||||
border:'1px solid rgba(255,255,255,0.08)', borderRadius:8, padding:'8px 10px',
|
||||
background: l.success ? 'transparent' : 'rgba(248,113,113,0.06)',
|
||||
|
||||
Reference in New Issue
Block a user