fix(admin): resolve login issues, fix media loading limit, and add bulk publish/unpublish buttons

This commit is contained in:
Jannik committed 2026-09-24 03:50:08 +02:00
1 parent 8b1843159f
commit b4cd3e6a71
5 files changed
+79 -10

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function main() {
try {
const totalCount = await prisma.mediaItem.count();
const publishedCount = await prisma.mediaItem.count({ where: { published: true } });
const dirs = await prisma.mediaItem.findMany({
where: { published: true },
select: { directory: true },
distinct: ['directory']
});
console.log("=== DATENBANK-CHECK ===");
console.log(`Bilder gesamt: ${totalCount}`);
console.log(`Bilder freigegeben (published): ${publishedCount}`);
console.log(`Gefundene Ordner (published): ${JSON.stringify(dirs)}`);
const allDirs = await prisma.mediaItem.findMany({
select: { directory: true },
distinct: ['directory']
});
console.log(`Gefundene Ordner (alle Bilder, auch unveröffentlicht): ${JSON.stringify(allDirs)}`);
} catch (err) {
console.error("Fehler beim Abfragen:", err);
} finally {
await prisma.$disconnect();
}
}
main();
+1 -1
View File
@@ -22,7 +22,7 @@ export default function AdminLoginPage() {
});
if (res.ok) {
router.push('/admin');
window.location.href = '/admin';
} else {
if (res.status === 429) {
setError('Zu viele Fehlversuche! Bitte warte 3 Minuten.');
+43 -5
View File
@@ -40,7 +40,7 @@ export default function AdminDashboard() {
setLoading(true);
try {
// Fetch all media (admin=true bypasses publication check)
const res = await fetch('/api/media?admin=true&limit=1000');
const res = await fetch('/api/media?admin=true&limit=5000');
if (res.ok) {
const data = await res.json();
setItems(data.items);
@@ -113,6 +113,36 @@ export default function AdminDashboard() {
}
};
const handleBulkAction = async (action: 'publish' | 'unpublish') => {
if (!confirm(`Möchtest du wirklich alle ${action === 'publish' ? 'freigeben' : 'aufheben'}?`)) return;
const isPublished = action === 'publish';
const idsToUpdate = items.filter(i => i.published !== isPublished).map(i => i.id);
if (idsToUpdate.length === 0) return;
// Optimistic UI
setItems((prev) =>
prev.map((i) => ({ ...i, published: isPublished }))
);
setStats({
total: items.length,
published: isPublished ? items.length : 0,
unpublished: isPublished ? 0 : items.length,
});
try {
const res = await fetch('/api/admin/media/bulk', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids: idsToUpdate, action }),
});
if (!res.ok) throw new Error('Bulk update failed');
} catch {
fetchData(); // Revert on error
}
};
if (!authChecked) return <div style={{ padding: '20px' }}>Prüfe Berechtigung...</div>;
return (
@@ -147,11 +177,19 @@ export default function AdminDashboard() {
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px', flexWrap: 'wrap', gap: '10px' }}>
<h3 style={{ fontSize: '1.2rem' }}>Alle Medien</h3>
<button className="btn-secondary" onClick={fetchData} disabled={loading}>
{loading ? 'Lädt...' : 'Aktualisieren'}
</button>
<div style={{ display: 'flex', gap: '10px' }}>
<button className="btn-secondary" onClick={() => handleBulkAction('publish')} disabled={loading || items.length === 0}>
Alle freigeben
</button>
<button className="btn-secondary" onClick={() => handleBulkAction('unpublish')} disabled={loading || items.length === 0}>
Alle aufheben
</button>
<button className="btn-secondary" onClick={fetchData} disabled={loading}>
{loading ? 'Lädt...' : 'Aktualisieren'}
</button>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: '15px' }}>
+3 -3
View File
@@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from 'next/server';
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import { validateSession } from '@/lib/auth/session';
@@ -9,10 +9,10 @@ export async function GET(request: NextRequest) {
const limitParam = searchParams.get('limit');
const adminParam = searchParams.get('admin');
const directoryParam = searchParams.get('directory');
const limit = limitParam ? Math.min(parseInt(limitParam, 10) || 50, 100) : 50;
const isAdminRequest = adminParam === 'true';
const limit = limitParam ? Math.min(parseInt(limitParam, 10) || 50, isAdminRequest ? 5000 : 100) : 50;
if (isAdminRequest) {
const isAuthenticated = await validateSession(request);
if (!isAuthenticated) {
+1 -1
View File
@@ -32,7 +32,7 @@ export interface RateLimitConfig {
export const RATE_LIMITS = {
login: {
maxRequests: 5,
maxRequests: 50,
windowSeconds: 3 * 60, // 3 minutes
prefix: 'rl:login',
},