Refactor: Zentralisiertes Admin-Login für alle Tabs
Admin-Auth aus Soundboard-Plugin in core/auth.ts extrahiert. Ein Login-Button im Header gilt jetzt für die gesamte Webseite. Cookie-basiert (HMAC-SHA256, 7 Tage) — überlebt Page-Reload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8abe0775a5
commit
b3080fb763
6 changed files with 101 additions and 133 deletions
|
|
@ -186,24 +186,6 @@ async function apiGetVolume(guildId: string): Promise<number> {
|
|||
return typeof data?.volume === 'number' ? data.volume : 1;
|
||||
}
|
||||
|
||||
async function apiAdminStatus(): Promise<boolean> {
|
||||
const res = await fetch(`${API_BASE}/admin/status`, { credentials: 'include' });
|
||||
if (!res.ok) return false;
|
||||
const data = await res.json();
|
||||
return !!data?.authenticated;
|
||||
}
|
||||
|
||||
async function apiAdminLogin(password: string): Promise<boolean> {
|
||||
const res = await fetch(`${API_BASE}/admin/login`, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include',
|
||||
body: JSON.stringify({ password })
|
||||
});
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
async function apiAdminLogout(): Promise<void> {
|
||||
await fetch(`${API_BASE}/admin/logout`, { method: 'POST', credentials: 'include' });
|
||||
}
|
||||
|
||||
async function apiAdminDelete(paths: string[]): Promise<void> {
|
||||
const res = await fetch(`${API_BASE}/admin/sounds/delete`, {
|
||||
|
|
@ -324,13 +306,14 @@ interface VoiceStats {
|
|||
|
||||
interface SoundboardTabProps {
|
||||
data: any;
|
||||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════
|
||||
COMPONENT
|
||||
══════════════════════════════════════════════════════════════════ */
|
||||
|
||||
export default function SoundboardTab({ data }: SoundboardTabProps) {
|
||||
export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: SoundboardTabProps) {
|
||||
/* ── Data ── */
|
||||
const [sounds, setSounds] = useState<Sound[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
|
|
@ -378,9 +361,8 @@ export default function SoundboardTab({ data }: SoundboardTabProps) {
|
|||
const volDebounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
|
||||
/* ── Admin ── */
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const isAdmin = isAdminProp;
|
||||
const [showAdmin, setShowAdmin] = useState(false);
|
||||
const [adminPwd, setAdminPwd] = useState('');
|
||||
const [adminSounds, setAdminSounds] = useState<Sound[]>([]);
|
||||
const [adminLoading, setAdminLoading] = useState(false);
|
||||
const [adminQuery, setAdminQuery] = useState('');
|
||||
|
|
@ -521,7 +503,6 @@ export default function SoundboardTab({ data }: SoundboardTabProps) {
|
|||
setSelected(match ? `${g}:${serverCid}` : `${ch[0].guildId}:${ch[0].channelId}`);
|
||||
}
|
||||
} catch (e: any) { notify(e?.message || 'Channel-Fehler', 'error'); }
|
||||
try { setIsAdmin(await apiAdminStatus()); } catch { }
|
||||
try { const c = await fetchCategories(); setCategories(c.categories || []); } catch { }
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -879,27 +860,6 @@ export default function SoundboardTab({ data }: SoundboardTabProps) {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleAdminLogin() {
|
||||
try {
|
||||
const ok = await apiAdminLogin(adminPwd);
|
||||
if (ok) {
|
||||
setIsAdmin(true);
|
||||
setAdminPwd('');
|
||||
notify('Admin eingeloggt');
|
||||
}
|
||||
else notify('Falsches Passwort', 'error');
|
||||
} catch { notify('Login fehlgeschlagen', 'error'); }
|
||||
}
|
||||
|
||||
async function handleAdminLogout() {
|
||||
try {
|
||||
await apiAdminLogout();
|
||||
setIsAdmin(false);
|
||||
setAdminSelection({});
|
||||
cancelRename();
|
||||
notify('Ausgeloggt');
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/* ── Computed ── */
|
||||
const displaySounds = useMemo(() => {
|
||||
|
|
@ -1447,21 +1407,6 @@ export default function SoundboardTab({ data }: SoundboardTabProps) {
|
|||
<span className="material-icons" style={{ fontSize: 18 }}>close</span>
|
||||
</button>
|
||||
</h3>
|
||||
{!isAdmin ? (
|
||||
<div>
|
||||
<div className="admin-field">
|
||||
<label>Passwort</label>
|
||||
<input
|
||||
type="password"
|
||||
value={adminPwd}
|
||||
onChange={e => setAdminPwd(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && handleAdminLogin()}
|
||||
placeholder="Admin-Passwort..."
|
||||
/>
|
||||
</div>
|
||||
<button className="admin-btn-action primary" onClick={handleAdminLogin}>Login</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="admin-shell">
|
||||
<div className="admin-header-row">
|
||||
<p className="admin-status">Eingeloggt als Admin</p>
|
||||
|
|
@ -1473,7 +1418,6 @@ export default function SoundboardTab({ data }: SoundboardTabProps) {
|
|||
>
|
||||
Aktualisieren
|
||||
</button>
|
||||
<button className="admin-btn-action outline" onClick={handleAdminLogout}>Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -1585,7 +1529,6 @@ export default function SoundboardTab({ data }: SoundboardTabProps) {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue