Refactor: Zentralisiertes Admin-Login im Top-Menü
- Admin-Login aus 3 Plugins (Soundboard, Streaming, Game Library) entfernt - Zentraler 🔒/🔓 Button im Header mit Login-Modal - isAdmin wird als Prop an alle Plugins weitergegeben - Settings-Buttons (Gear-Icons) nur sichtbar wenn eingeloggt - Alle Plugins nutzen weiterhin den shared admin-Cookie für Operationen - Login/Logout-Formulare und Buttons aus Plugin-Panels entfernt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bccfee3de2
commit
e9931d82af
10 changed files with 5077 additions and 5063 deletions
|
|
@ -13,7 +13,7 @@ interface PluginInfo {
|
|||
}
|
||||
|
||||
// Plugin tab components
|
||||
const tabComponents: Record<string, React.FC<{ data: any }>> = {
|
||||
const tabComponents: Record<string, React.FC<{ data: any; isAdmin?: boolean }>> = {
|
||||
radio: RadioTab,
|
||||
soundboard: SoundboardTab,
|
||||
lolstats: LolstatsTab,
|
||||
|
|
@ -22,7 +22,7 @@ const tabComponents: Record<string, React.FC<{ data: any }>> = {
|
|||
'game-library': GameLibraryTab,
|
||||
};
|
||||
|
||||
export function registerTab(pluginName: string, component: React.FC<{ data: any }>) {
|
||||
export function registerTab(pluginName: string, component: React.FC<{ data: any; isAdmin?: boolean }>) {
|
||||
tabComponents[pluginName] = component;
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +40,12 @@ export default function App() {
|
|||
const [showVersionModal, setShowVersionModal] = useState(false);
|
||||
const [pluginData, setPluginData] = useState<Record<string, any>>({});
|
||||
|
||||
// Centralized admin login state
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [showAdminLogin, setShowAdminLogin] = useState(false);
|
||||
const [adminPwd, setAdminPwd] = useState('');
|
||||
const [adminError, setAdminError] = useState('');
|
||||
|
||||
// Electron auto-update state
|
||||
const isElectron = !!(window as any).electronAPI?.isElectron;
|
||||
const electronVersion = isElectron ? (window as any).electronAPI.version : null;
|
||||
|
|
@ -54,6 +60,48 @@ export default function App() {
|
|||
}
|
||||
}, []);
|
||||
|
||||
// Check admin status on mount (shared cookie — any endpoint works)
|
||||
useEffect(() => {
|
||||
fetch('/api/soundboard/admin/status', { credentials: 'include' })
|
||||
.then(r => r.json())
|
||||
.then(d => setIsAdmin(!!d.authenticated))
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
// Escape key closes admin login modal
|
||||
useEffect(() => {
|
||||
if (!showAdminLogin) return;
|
||||
const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') setShowAdminLogin(false); };
|
||||
window.addEventListener('keydown', handler);
|
||||
return () => window.removeEventListener('keydown', handler);
|
||||
}, [showAdminLogin]);
|
||||
|
||||
async function handleAdminLogin() {
|
||||
setAdminError('');
|
||||
try {
|
||||
const resp = await fetch('/api/soundboard/admin/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ password: adminPwd }),
|
||||
credentials: 'include',
|
||||
});
|
||||
if (resp.ok) {
|
||||
setIsAdmin(true);
|
||||
setAdminPwd('');
|
||||
setShowAdminLogin(false);
|
||||
} else {
|
||||
setAdminError('Falsches Passwort');
|
||||
}
|
||||
} catch {
|
||||
setAdminError('Verbindung fehlgeschlagen');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAdminLogout() {
|
||||
await fetch('/api/soundboard/admin/logout', { method: 'POST', credentials: 'include' });
|
||||
setIsAdmin(false);
|
||||
}
|
||||
|
||||
// Electron auto-update listeners
|
||||
useEffect(() => {
|
||||
if (!isElectron) return;
|
||||
|
|
@ -188,6 +236,13 @@ export default function App() {
|
|||
<span className="hub-download-label">Desktop App</span>
|
||||
</a>
|
||||
)}
|
||||
<button
|
||||
className={`hub-admin-btn ${isAdmin ? 'active' : ''}`}
|
||||
onClick={() => isAdmin ? handleAdminLogout() : setShowAdminLogin(true)}
|
||||
title={isAdmin ? 'Admin abmelden' : 'Admin Login'}
|
||||
>
|
||||
{isAdmin ? '\uD83D\uDD13' : '\uD83D\uDD12'}
|
||||
</button>
|
||||
<button
|
||||
className="hub-refresh-btn"
|
||||
onClick={() => window.location.reload()}
|
||||
|
|
@ -307,6 +362,34 @@ export default function App() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{showAdminLogin && (
|
||||
<div className="hub-admin-overlay" onClick={() => setShowAdminLogin(false)}>
|
||||
<div className="hub-admin-modal" onClick={e => e.stopPropagation()}>
|
||||
<div className="hub-admin-modal-header">
|
||||
<span>{'\uD83D\uDD12'} Admin Login</span>
|
||||
<button className="hub-admin-modal-close" onClick={() => setShowAdminLogin(false)}>
|
||||
{'\u2715'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="hub-admin-modal-body">
|
||||
<input
|
||||
type="password"
|
||||
className="hub-admin-input"
|
||||
placeholder="Admin-Passwort..."
|
||||
value={adminPwd}
|
||||
onChange={e => setAdminPwd(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && handleAdminLogin()}
|
||||
autoFocus
|
||||
/>
|
||||
{adminError && <p className="hub-admin-error">{adminError}</p>}
|
||||
<button className="hub-admin-submit" onClick={handleAdminLogin}>
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<main className="hub-content">
|
||||
{plugins.length === 0 ? (
|
||||
<div className="hub-empty">
|
||||
|
|
@ -330,7 +413,7 @@ export default function App() {
|
|||
: { display: 'none' }
|
||||
}
|
||||
>
|
||||
<Comp data={pluginData[p.name] || {}} />
|
||||
<Comp data={pluginData[p.name] || {}} isAdmin={isAdmin} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue