From 3a2efd97c322b6393b19ec78124cfda266467e16 Mon Sep 17 00:00:00 2001 From: "matthias.lotz" Date: Sat, 8 Nov 2025 12:55:55 +0100 Subject: [PATCH] refactor: Move deletion log into ModerationGroupsPage - Create DeletionLogSection component - Integrate deletion log at bottom of moderation page - Remove standalone DeletionLogPage and route - Remove admin nav link (log now in moderation) - Keep /api/admin routes for backend API access - Update nginx configs (remove /admin frontend route) --- docker/dev/frontend/nginx.conf | 14 - docker/prod/frontend/nginx.conf | 14 - frontend/src/App.js | 2 - .../ComponentUtils/DeletionLogSection.js | 264 ++++++++++++++++ .../ComponentUtils/Headers/Navbar.js | 3 +- .../src/Components/Pages/DeletionLogPage.js | 285 ------------------ .../Components/Pages/ModerationGroupsPage.js | 6 + 7 files changed, 271 insertions(+), 317 deletions(-) create mode 100644 frontend/src/Components/ComponentUtils/DeletionLogSection.js delete mode 100644 frontend/src/Components/Pages/DeletionLogPage.js diff --git a/docker/dev/frontend/nginx.conf b/docker/dev/frontend/nginx.conf index 33c7a0d..f36c6aa 100644 --- a/docker/dev/frontend/nginx.conf +++ b/docker/dev/frontend/nginx.conf @@ -107,20 +107,6 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } - # Protected routes - Admin (password protected) - React Dev Server - location /admin { - auth_basic "Restricted Area - Admin"; - auth_basic_user_file /etc/nginx/.htpasswd; - - proxy_pass http://127.0.0.1:3000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - # Protected routes - Moderation (password protected) - React Dev Server location /moderation { auth_basic "Restricted Area - Moderation"; diff --git a/docker/prod/frontend/nginx.conf b/docker/prod/frontend/nginx.conf index d5f8c3d..eba988c 100644 --- a/docker/prod/frontend/nginx.conf +++ b/docker/prod/frontend/nginx.conf @@ -141,20 +141,6 @@ http { add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; } - # Protected routes - Admin (password protected) - location /admin { - auth_basic "Restricted Area - Admin"; - auth_basic_user_file /etc/nginx/.htpasswd; - - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - expires -1; - - # Prevent indexing - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; - } - # Protected routes - Moderation (password protected) location /moderation { auth_basic "Restricted Area - Moderation"; diff --git a/frontend/src/App.js b/frontend/src/App.js index f2c8fc3..9f60ec2 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -8,7 +8,6 @@ import GroupsOverviewPage from './Components/Pages/GroupsOverviewPage'; import ModerationGroupsPage from './Components/Pages/ModerationGroupsPage'; import ModerationGroupImagesPage from './Components/Pages/ModerationGroupImagesPage'; import PublicGroupImagesPage from './Components/Pages/PublicGroupImagesPage'; -import DeletionLogPage from './Components/Pages/DeletionLogPage'; import FZF from './Components/Pages/404Page.js' function App() { @@ -21,7 +20,6 @@ function App() { } /> } /> } /> - } /> } /> diff --git a/frontend/src/Components/ComponentUtils/DeletionLogSection.js b/frontend/src/Components/ComponentUtils/DeletionLogSection.js new file mode 100644 index 0000000..4af3458 --- /dev/null +++ b/frontend/src/Components/ComponentUtils/DeletionLogSection.js @@ -0,0 +1,264 @@ +import React, { useState, useEffect } from 'react'; +import { + Card, + Typography, + Button, + Box, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Chip, + Grid, + CircularProgress +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import WarningIcon from '@mui/icons-material/Warning'; +import InfoIcon from '@mui/icons-material/Info'; + +const DeletionLogSection = () => { + const [deletions, setDeletions] = useState([]); + const [statistics, setStatistics] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [showAll, setShowAll] = useState(false); + + useEffect(() => { + loadDeletionLog(); + loadStatistics(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showAll]); + + const loadDeletionLog = async () => { + try { + setLoading(true); + const endpoint = showAll + ? '/api/admin/deletion-log/all' + : '/api/admin/deletion-log?limit=10'; + + const response = await fetch(endpoint); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + setDeletions(data.deletions || []); + setError(null); + } catch (error) { + console.error('Fehler beim Laden des Lösch-Logs:', error); + setError('Fehler beim Laden des Lösch-Logs'); + } finally { + setLoading(false); + } + }; + + const loadStatistics = async () => { + try { + const response = await fetch('/api/admin/deletion-log/stats'); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + setStatistics(data.statistics || null); + } catch (error) { + console.error('Fehler beim Laden der Statistiken:', error); + } + }; + + const formatDate = (dateString) => { + if (!dateString) return '-'; + const date = new Date(dateString); + return date.toLocaleString('de-DE', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }); + }; + + const formatFileSize = (bytes) => { + if (!bytes || bytes === 0) return '0 KB'; + + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`; + }; + + const getReasonIcon = (reason) => { + if (reason && reason.includes('unapproved')) { + return ; + } + return ; + }; + + if (loading) { + return ( + + + + Lade Lösch-Historie... + + + ); + } + + return ( + + + + + Lösch-Historie + + + Automatisch gelöschte Gruppen (nicht innerhalb von 7 Tagen freigegeben) + + + + {error && ( + + {error} + + )} + + {/* Statistics Cards */} + {statistics && ( + + + + + {statistics.totalGroupsDeleted || 0} + + + Gelöschte Gruppen + + + + + + + {statistics.totalImagesDeleted || 0} + + + Gelöschte Bilder + + + + + + + {statistics.totalStorageFreed || '0 KB'} + + + Speicher freigegeben + + + + + )} + + {/* Toggle Button */} + + + {showAll ? 'Alle Einträge' : 'Letzte 10 Einträge'} + + + + + {/* Deletion Log Table */} + {deletions.length === 0 ? ( + + + + Keine Lösch-Einträge gefunden + + + Es wurden bisher keine Gruppen automatisch gelöscht. + + + ) : ( + + + + + Gruppe ID + Jahr + Bilder + Upload-Datum + Gelöscht am + Grund + Größe + + + + {deletions.map((row) => ( + + + + + {row.year || '-'} + {row.image_count || 0} + {formatDate(row.upload_date)} + {formatDate(row.deleted_at)} + + + {getReasonIcon(row.deletion_reason)} + + {row.deletion_reason || 'Unbekannt'} + + + + + + {formatFileSize(row.total_file_size)} + + + + ))} + +
+
+ )} + + {/* Info Box */} + + + + + + Automatische Löschung + + + Der Cleanup läuft täglich um 10:00 Uhr. Gruppen, die nicht innerhalb von 7 Tagen + freigegeben werden, werden automatisch gelöscht. Alle Lösch-Vorgänge werden hier protokolliert. + + + + +
+ ); +}; + +export default DeletionLogSection; diff --git a/frontend/src/Components/ComponentUtils/Headers/Navbar.js b/frontend/src/Components/ComponentUtils/Headers/Navbar.js index 968cde7..bc8f5a7 100644 --- a/frontend/src/Components/ComponentUtils/Headers/Navbar.js +++ b/frontend/src/Components/ComponentUtils/Headers/Navbar.js @@ -4,7 +4,7 @@ import { NavLink } from 'react-router-dom' import '../Css/Navbar.css' import logo from '../../../Images/logo.png' -import { Lock as LockIcon, AdminPanelSettings as AdminIcon } from '@mui/icons-material'; +import { Lock as LockIcon } from '@mui/icons-material'; function Navbar() { return ( @@ -15,7 +15,6 @@ function Navbar() {
  • Groups
  • Slideshow
  • -
  • Upload
  • About
  • diff --git a/frontend/src/Components/Pages/DeletionLogPage.js b/frontend/src/Components/Pages/DeletionLogPage.js deleted file mode 100644 index bd4ae29..0000000 --- a/frontend/src/Components/Pages/DeletionLogPage.js +++ /dev/null @@ -1,285 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Helmet } from 'react-helmet'; -import { - Container, - Card, - Typography, - Button, - Box, - CircularProgress, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Paper, - Chip, - Grid -} from '@mui/material'; -import DeleteIcon from '@mui/icons-material/Delete'; -import WarningIcon from '@mui/icons-material/Warning'; -import InfoIcon from '@mui/icons-material/Info'; -import Navbar from '../ComponentUtils/Headers/Navbar'; -import Footer from '../ComponentUtils/Footer'; - -const DeletionLogPage = () => { - const [deletions, setDeletions] = useState([]); - const [statistics, setStatistics] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [showAll, setShowAll] = useState(false); - - useEffect(() => { - loadDeletionLog(); - loadStatistics(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [showAll]); - - const loadDeletionLog = async () => { - try { - setLoading(true); - const endpoint = showAll - ? '/api/admin/deletion-log/all' - : '/api/admin/deletion-log?limit=10'; - - const response = await fetch(endpoint); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - setDeletions(data.deletions || []); - setError(null); - } catch (error) { - console.error('Fehler beim Laden des Lösch-Logs:', error); - setError('Fehler beim Laden des Lösch-Logs'); - } finally { - setLoading(false); - } - }; - - const loadStatistics = async () => { - try { - const response = await fetch('/api/admin/deletion-log/stats'); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - setStatistics(data.statistics || null); - } catch (error) { - console.error('Fehler beim Laden der Statistiken:', error); - } - }; - - const formatDate = (dateString) => { - if (!dateString) return '-'; - const date = new Date(dateString); - return date.toLocaleString('de-DE', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit' - }); - }; - - const getReasonIcon = (reason) => { - if (reason.includes('unapproved')) { - return ; - } - return ; - }; - - if (loading) { - return ( -
    - - -
    - - - Lade Lösch-Historie... - -
    -
    -
    -
    - ); - } - - return ( -
    - - Lösch-Historie - Image Uploader - - - - - - - Lösch-Historie - - - Übersicht über automatisch gelöschte Gruppen - - - - {error && ( - - {error} - - )} - - {/* Statistics Cards */} - {statistics && ( - - - - - {statistics.totalGroupsDeleted || 0} - - - Gelöschte Gruppen - - - - - - - {statistics.totalImagesDeleted || 0} - - - Gelöschte Bilder - - - - - - - {statistics.totalStorageFreed || '0 KB'} - - - Speicher freigegeben - - - - - )} - - {/* Toggle Button */} - - - {showAll ? 'Alle Einträge' : 'Letzte 10 Einträge'} - - - - - {/* Deletion Log Table */} - {deletions.length === 0 ? ( - - - - Keine Lösch-Einträge gefunden - - - Es wurden bisher keine Gruppen automatisch gelöscht. - - - ) : ( - - - - - Gruppe ID - Jahr - Bilder - Upload-Datum - Gelöscht am - Grund - Größe - - - - {deletions.map((row) => ( - - - - - {row.year || '-'} - {row.image_count || 0} - {formatDate(row.upload_date)} - {formatDate(row.deleted_at)} - - - {getReasonIcon(row.deletion_reason)} - - {row.deletion_reason || 'Unbekannt'} - - - - - - {formatFileSize(row.total_file_size)} - - - - ))} - -
    -
    - )} - - {/* Info Box */} - - - - - - Hinweis zur automatischen Löschung - - - Gruppen, die nicht innerhalb von 7 Tagen nach dem Upload freigegeben werden, - werden automatisch gelöscht. Der Cleanup läuft täglich um 10:00 Uhr. - Alle Lösch-Vorgänge werden hier protokolliert (ohne personenbezogene Daten). - - - - -
    -
    -
    - ); -}; - -// Helper function for file size formatting -const formatFileSize = (bytes) => { - if (!bytes || bytes === 0) return '0 KB'; - - const k = 1024; - const sizes = ['Bytes', 'KB', 'MB', 'GB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - - return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`; -}; - -export default DeletionLogPage; diff --git a/frontend/src/Components/Pages/ModerationGroupsPage.js b/frontend/src/Components/Pages/ModerationGroupsPage.js index edf82c6..d26fe8e 100644 --- a/frontend/src/Components/Pages/ModerationGroupsPage.js +++ b/frontend/src/Components/Pages/ModerationGroupsPage.js @@ -6,6 +6,7 @@ import Swal from 'sweetalert2/dist/sweetalert2.js'; import Navbar from '../ComponentUtils/Headers/Navbar'; import Footer from '../ComponentUtils/Footer'; import ImageGallery from '../ComponentUtils/ImageGallery'; +import DeletionLogSection from '../ComponentUtils/DeletionLogSection'; import { getImageSrc } from '../../Utils/imageUtils'; const ModerationGroupsPage = () => { @@ -221,6 +222,11 @@ const ModerationGroupsPage = () => { /> + {/* Lösch-Historie */} +
    + +
    + {/* Bilder-Modal */} {showImages && selectedGroup && (