fix(admin): resolve login issues, fix media loading limit, and add bulk publish/unpublish buttons
This commit is contained in:
1 parent
8b1843159f
commit
b4cd3e6a71
5 files changed
+79
-10
No files matched your search
+31
@@ -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();
|
||||
@@ -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
@@ -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' }}>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -32,7 +32,7 @@ export interface RateLimitConfig {
|
||||
|
||||
export const RATE_LIMITS = {
|
||||
login: {
|
||||
maxRequests: 5,
|
||||
maxRequests: 50,
|
||||
windowSeconds: 3 * 60, // 3 minutes
|
||||
prefix: 'rl:login',
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user