diff --git a/TODO.md b/TODO.md index fde28d4..7807c78 100644 --- a/TODO.md +++ b/TODO.md @@ -23,7 +23,7 @@ Neue Struktur: Datenbank in src/data/db und bilder in src/data/images ### Frontend -- [ ] Code Cleanup & Refactoring +- [x] Code Cleanup & Refactoring - [x] Überprüfung der Komponentenstruktur - [x] Entfernen ungenutzter Dateien - [x] Vereinheitlichung der ImageGallery Komponente: diff --git a/backend/src/repositories/GroupRepository.js b/backend/src/repositories/GroupRepository.js index 718c17d..391d9b7 100644 --- a/backend/src/repositories/GroupRepository.js +++ b/backend/src/repositories/GroupRepository.js @@ -252,19 +252,25 @@ class GroupRepository { // Alle Gruppen für Moderation (mit Freigabestatus und Bildanzahl) async getAllGroupsWithModerationInfo() { + const groupFormatter = require('../utils/groupFormatter'); + const groups = await dbManager.all(` - SELECT - g.*, - COUNT(i.id) as image_count, - MIN(i.file_path) as preview_image - FROM groups g - LEFT JOIN images i ON g.group_id = i.group_id - GROUP BY g.group_id - ORDER BY g.approved ASC, g.upload_date DESC + SELECT * FROM groups + ORDER BY approved ASC, upload_date DESC `); - const groupFormatter = require('../utils/groupFormatter'); - return groups.map(group => groupFormatter.formatGroupListRow(group)); + const result = []; + for (const group of groups) { + const images = await dbManager.all(` + SELECT * FROM images + WHERE group_id = ? + ORDER BY upload_order ASC + `, [group.group_id]); + + result.push(groupFormatter.formatGroupDetail(group, images)); + } + + return result; } // Hole Gruppe für Moderation (inkl. nicht-freigegebene) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 7534170..93bfedd 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,5 +1,3 @@ -version: '3.8' - # Development override to mount the frontend source into a node container # and run the React dev server with HMR so you can edit files locally # without rebuilding images. This file is intended to be used together diff --git a/frontend/conf/conf.d/default.conf b/frontend/conf/conf.d/default.conf index c0ff033..0617d86 100644 --- a/frontend/conf/conf.d/default.conf +++ b/frontend/conf/conf.d/default.conf @@ -28,6 +28,24 @@ server { client_max_body_size 100M; } + # API - Download original images + location /api/download { + proxy_pass http://image-uploader-backend:5000/download; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API - Preview/thumbnail images (optimized for gallery views) + location /api/previews { + proxy_pass http://image-uploader-backend:5000/previews; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + # API - Groups (NO PASSWORD PROTECTION) location /api/groups { proxy_pass http://image-uploader-backend:5000/groups; diff --git a/frontend/conf/conf.d/default.conf.backup b/frontend/conf/conf.d/default.conf.backup index c0ff033..0617d86 100644 --- a/frontend/conf/conf.d/default.conf.backup +++ b/frontend/conf/conf.d/default.conf.backup @@ -28,6 +28,24 @@ server { client_max_body_size 100M; } + # API - Download original images + location /api/download { + proxy_pass http://image-uploader-backend:5000/download; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API - Preview/thumbnail images (optimized for gallery views) + location /api/previews { + proxy_pass http://image-uploader-backend:5000/previews; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + # API - Groups (NO PASSWORD PROTECTION) location /api/groups { proxy_pass http://image-uploader-backend:5000/groups; diff --git a/frontend/nginx.dev.conf b/frontend/nginx.dev.conf index 5bfdb2e..a39328b 100644 --- a/frontend/nginx.dev.conf +++ b/frontend/nginx.dev.conf @@ -2,6 +2,68 @@ server { listen 80; server_name localhost; + # API proxy to backend - must come before / location + # Upload endpoint + location /api/upload { + proxy_pass http://image-uploader-backend:5000/upload; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + client_max_body_size 100M; + } + + # Download original images + location /api/download { + proxy_pass http://image-uploader-backend:5000/download; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Preview/thumbnail images (optimized for gallery views) + location /api/previews { + proxy_pass http://image-uploader-backend:5000/previews; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Groups API + location /api/groups { + proxy_pass http://image-uploader-backend:5000/groups; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Moderation API (groups) + location /moderation/groups { + proxy_pass http://image-uploader-backend:5000/moderation/groups; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Groups routes (both API and page routes) + location /groups { + # Try to serve as static file first, then proxy to React dev server + try_files $uri @proxy; + } + + # Download endpoint (legacy, without /api prefix) + location /download { + proxy_pass http://image-uploader-backend:5000/download; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + # Proxy requests to the CRA dev server so nginx can be used as reverse proxy location /sockjs-node/ { proxy_pass http://127.0.0.1:3000; @@ -21,6 +83,16 @@ server { proxy_set_header Host $host; } + location @proxy { + 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; + } + location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; diff --git a/frontend/src/Components/ComponentUtils/ImageGalleryCard.js b/frontend/src/Components/ComponentUtils/ImageGalleryCard.js index 74589c3..9cfb3e8 100644 --- a/frontend/src/Components/ComponentUtils/ImageGalleryCard.js +++ b/frontend/src/Components/ComponentUtils/ImageGalleryCard.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import './Css/ImageGallery.css'; +import { getImageSrc, getGroupPreviewSrc } from '../../Utils/imageUtils'; const ImageGalleryCard = ({ item, @@ -25,13 +26,8 @@ const ImageGalleryCard = ({ if (mode === 'preview' || mode === 'single-image') { // Preview mode: display individual images - if (item.remoteUrl) { - previewUrl = item.remoteUrl; - } else if (item.url) { - previewUrl = item.url; - } else if (item.filePath) { - previewUrl = item.filePath; - } + // Use preview image (optimized thumbnails for gallery) + previewUrl = getImageSrc(item, true); title = item.originalName || item.name || 'Bild'; @@ -45,11 +41,8 @@ const ImageGalleryCard = ({ // Group mode: display group information const group = item; - if (group.previewImage) { - previewUrl = `/download/${group.previewImage.split('/').pop()}`; - } else if (group.images && group.images.length > 0 && group.images[0].filePath) { - previewUrl = group.images[0].filePath; - } + // Use preview image from first image in group + previewUrl = getGroupPreviewSrc(group, true); title = group.title; subtitle = `${group.year} • ${group.name}`; diff --git a/frontend/src/Components/Pages/ModerationGroupImagesPage.js b/frontend/src/Components/Pages/ModerationGroupImagesPage.js index 3a3e418..23aecc5 100644 --- a/frontend/src/Components/Pages/ModerationGroupImagesPage.js +++ b/frontend/src/Components/Pages/ModerationGroupImagesPage.js @@ -41,7 +41,8 @@ const ModerationGroupImagesPage = () => { // Map group's images to preview-friendly objects if (data.images && data.images.length > 0) { const mapped = data.images.map(img => ({ - remoteUrl: `/download/${img.fileName}`, + ...img, // Pass all image fields including previewPath + remoteUrl: `/download/${img.fileName}`, // Keep for backward compatibility originalName: img.originalName || img.fileName, id: img.id })); diff --git a/frontend/src/Components/Pages/ModerationGroupsPage.js b/frontend/src/Components/Pages/ModerationGroupsPage.js index 9b5b530..5ec12ac 100644 --- a/frontend/src/Components/Pages/ModerationGroupsPage.js +++ b/frontend/src/Components/Pages/ModerationGroupsPage.js @@ -5,6 +5,7 @@ import { Container } from '@mui/material'; import Navbar from '../ComponentUtils/Headers/Navbar'; import Footer from '../ComponentUtils/Footer'; import ImageGallery from '../ComponentUtils/ImageGallery'; +import { getImageSrc } from '../../Utils/imageUtils'; const ModerationGroupsPage = () => { const [groups, setGroups] = useState([]); @@ -246,7 +247,7 @@ const ImageModal = ({ group, onClose, onDeleteImage }) => { {group.images.map(image => (