Upload-Freigabe: öffentlicher Upload-Link mit Passwort, Ablaufzeit, max. Größe; Admin-Verwaltung
This commit is contained in:
@@ -1481,7 +1481,6 @@ function SearchResultItem({ item, idx, activeIdx, onNavigate, onHover }) {
|
||||
if (item.type==='calendar_feed') { icon='📅'; primary=item.name; secondary='Kalender-Abo'; }
|
||||
if (item.type==='calendar_event') { icon='📆'; primary=item.summary; secondary=`${item.feed_name||'Kalender'}${item.start_dt?' · '+formatIcalDate(item.start_dt):''}`; }
|
||||
if (item.type==='qr') { icon='◻'; primary=item.label||item.url; secondary=`QR-Code · ${item.url}`; }
|
||||
if (item.type==='user') { icon='💬'; primary=item.username; secondary='Nachricht schreiben → Chat öffnen'; }
|
||||
return (
|
||||
<div onClick={()=>onNavigate(item)} onMouseEnter={()=>onHover(idx)}
|
||||
style={{display:'flex',alignItems:'center',gap:10,padding:'9px 14px',cursor:'pointer',
|
||||
@@ -1643,7 +1642,6 @@ function GlobalSearch({ setActive, setDevToolsNav, mobile }) {
|
||||
...(results.calendar_feeds||[]).map(f=>({...f,type:'calendar_feed'})),
|
||||
...(results.calendar_events||[]).map(e=>({...e,type:'calendar_event'})),
|
||||
...(results.qr_codes||[]).map(q=>({...q,type:'qr'})),
|
||||
...(results.users||[]).map(u=>({...u,type:'user'})),
|
||||
] : [];
|
||||
|
||||
const navigate = (item) => {
|
||||
@@ -1663,8 +1661,7 @@ function GlobalSearch({ setActive, setDevToolsNav, mobile }) {
|
||||
else if (item.type === 'file' || item.type === 'file_folder') { setActive('dateien'); }
|
||||
else if (item.type === 'push') { setActive('dashboard'); }
|
||||
else if (item.type === 'calendar_feed' || item.type === 'calendar_event') { setActive('dashboard'); }
|
||||
else if (item.type === 'qr') { setActive('devtools'); setDevToolsNav({tool:'qrcode', ts: Date.now()}); }
|
||||
else if (item.type === 'user') { setActive('nachrichten'); setDevToolsNav({userId: item.id, ts: Date.now()}); }
|
||||
else if (item.type === 'qr') { setActive('devtools'); setDevToolsNav({tool:'qrcode', ts: Date.now()}); }
|
||||
};
|
||||
|
||||
const onKey = e => {
|
||||
@@ -1803,12 +1800,6 @@ function GlobalSearch({ setActive, setDevToolsNav, mobile }) {
|
||||
{results.qr_codes.map((item,i)=>(<SearchResultItem key={`qr${i}`} item={{...item,type:'qr'}} idx={0} activeIdx={-1} onNavigate={navigate} onHover={()=>{}}/>))}
|
||||
</div>
|
||||
)}
|
||||
{results.users?.length>0 && (
|
||||
<div>
|
||||
<div style={{padding:'7px 14px 4px',color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:9,letterSpacing:1}}>BENUTZER / CHAT</div>
|
||||
{results.users.map((item,i)=>(<SearchResultItem key={`u${i}`} item={{...item,type:'user'}} idx={0} activeIdx={-1} onNavigate={navigate} onHover={()=>{}}/>))}
|
||||
</div>
|
||||
)}
|
||||
{results.notes?.length>0 && (
|
||||
<div>
|
||||
<div style={{padding:'7px 14px 4px',color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:9,letterSpacing:1}}>NOTIZEN</div>
|
||||
@@ -2594,6 +2585,77 @@ const Field = ({ label, type='text', value, onChange, placeholder='', autoCapita
|
||||
</div>
|
||||
);
|
||||
|
||||
// ── Admin: Upload-Freigabe Berechtigungen ─────────────────────────────────────
|
||||
function UploadShareAdminPanel({ toast }) {
|
||||
const [users, setUsers] = useState([]);
|
||||
const [logs, setLogs] = useState([]);
|
||||
const [showLogs, setShowLogs] = useState(false);
|
||||
|
||||
const load = () => {
|
||||
api('/users').then(setUsers).catch(()=>{});
|
||||
};
|
||||
const loadLogs = () => {
|
||||
api('/upload-shares/admin/logs').then(setLogs).catch(()=>{});
|
||||
setShowLogs(true);
|
||||
};
|
||||
useEffect(()=>{ load(); },[]);
|
||||
|
||||
const toggle = async (uid, current) => {
|
||||
try {
|
||||
await api('/upload-shares/admin/permission', { body:{ user_id: uid, allow: !current } });
|
||||
setUsers(p => p.map(u => u.id===uid ? {...u, allow_upload_share: current?0:1} : u));
|
||||
toast(current ? 'Berechtigung entzogen' : 'Berechtigung erteilt');
|
||||
} catch(e) { toast(e.message,'error'); }
|
||||
};
|
||||
|
||||
const fmtDate = d => d ? new Date(d).toLocaleString('de-DE',{timeZone:'Europe/Berlin',day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) : '–';
|
||||
const fmtSize = b => b>1024*1024 ? (b/1024/1024).toFixed(1)+' MB' : (b/1024).toFixed(0)+' KB';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:12,marginBottom:12,lineHeight:1.7}}>
|
||||
Admins haben immer Zugriff auf Upload-Freigaben.<br/>
|
||||
Hier kannst du die Funktion für einzelne Mitglieder freischalten.
|
||||
</div>
|
||||
{users.filter(u=>u.role!=='admin').map(u => (
|
||||
<div key={u.id} style={{display:'flex',alignItems:'center',gap:10,padding:'8px 0',borderBottom:'1px solid rgba(255,255,255,0.06)'}}>
|
||||
<span style={{flex:1,color:'rgba(255,255,255,0.7)',fontFamily:'monospace',fontSize:13}}>{u.username}</span>
|
||||
<button onClick={()=>toggle(u.id, !!u.allow_upload_share)}
|
||||
style={{
|
||||
padding:'4px 14px',borderRadius:16,fontFamily:'monospace',fontSize:11,cursor:'pointer',
|
||||
background: u.allow_upload_share ? 'rgba(78,205,196,0.15)' : 'rgba(255,255,255,0.05)',
|
||||
border: `1px solid ${u.allow_upload_share ? '#4ecdc4' : 'rgba(255,255,255,0.15)'}`,
|
||||
color: u.allow_upload_share ? '#4ecdc4' : 'rgba(255,255,255,0.4)',
|
||||
}}>
|
||||
{u.allow_upload_share ? '● Erlaubt' : '○ Gesperrt'}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<button onClick={loadLogs} style={{...S.btn('#ffe66d',true),marginTop:14,fontSize:11}}>
|
||||
📋 Nutzungsprotokoll anzeigen
|
||||
</button>
|
||||
{showLogs && (
|
||||
<div style={{marginTop:12}}>
|
||||
<div style={{...S.head,marginBottom:8}}>ALLE UPLOADS ÜBER FREIGABE-LINKS</div>
|
||||
{logs.length===0
|
||||
? <div style={{color:'rgba(255,255,255,0.35)',fontFamily:'monospace',fontSize:11}}>Noch keine Uploads.</div>
|
||||
: logs.map(l=>(
|
||||
<div key={l.id} style={{display:'flex',gap:10,padding:'7px 0',borderBottom:'1px solid rgba(255,255,255,0.05)',flexWrap:'wrap'}}>
|
||||
<span style={{color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:11,flexShrink:0,minWidth:90}}>{l.username}</span>
|
||||
<div style={{flex:1,minWidth:120}}>
|
||||
<div style={{color:'#fff',fontFamily:'monospace',fontSize:12,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{l.originalname}</div>
|
||||
<div style={{color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:10}}>{fmtDate(l.uploaded_at)} · {fmtSize(l.size)} · Max: {l.max_size_mb} MB</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function AdminPanel({ toast, mobile, user, nav }) {
|
||||
const [section, setSection] = useState('profil');
|
||||
useEffect(() => { if (nav?.section) setSection(nav.section); }, [nav?.ts]);
|
||||
@@ -2740,6 +2802,9 @@ function AdminPanel({ toast, mobile, user, nav }) {
|
||||
<Sec title="DATEI-MANAGER LIMITS">
|
||||
<DateiSettings toast={toast}/>
|
||||
</Sec>
|
||||
<Sec title="UPLOAD-FREIGABE">
|
||||
<UploadShareAdminPanel toast={toast}/>
|
||||
</Sec>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2898,7 +2963,7 @@ export default function App() {
|
||||
<main style={mainStyle}>
|
||||
{active==='dashboard' && <Dashboard toast={toast} mobile={mobile} setActive={setActive} setDevToolsNav={setDevToolsNav} unreadMsgs={unreadMsgs} unreadBoard={unreadBoard} unreadPromoted={unreadPromoted} onBoardRead={()=>{ setUnreadBoard(0); setUnreadPromoted(0); }} unreadChangelog={unreadChangelog} onChangelogRead={()=>setUnreadChangelog(0)} user={user}/>}
|
||||
{active==='admin' && <AdminPanel toast={toast} mobile={mobile} user={user} nav={devToolsNav}/>}
|
||||
{activeTool && <activeTool.component toast={toast} mobile={mobile} setActive={setActive} {...(activeTool.id==='devtools'||activeTool.id==='nachrichten'?{nav:devToolsNav}:{})}/>}
|
||||
{activeTool && <activeTool.component toast={toast} mobile={mobile} setActive={setActive} {...(activeTool.id==='devtools'?{nav:devToolsNav}:{})}/>}
|
||||
</main>
|
||||
</div>
|
||||
{mobile && (
|
||||
|
||||
Reference in New Issue
Block a user