Fix batch upload and attempt nginx auth setup

- Fixed missing 'path' import in batchUpload.js
- Fixed GroupRepository import (singleton vs class)
- Added htpasswd file to development config
- Created new nginx.conf based on working production config
- Updated Dockerfile to copy htpasswd for development

Status:
 Upload functionality restored (both single and batch)
 Backend routing and error handling fixed
⚠️ nginx auth config needs troubleshooting (container not using new config)
This commit is contained in:
Matthias Lotz 2025-11-06 18:28:32 +01:00
parent 782f11e513
commit 3845de92a6
7 changed files with 464 additions and 112 deletions

View File

@ -7,15 +7,16 @@ class GroupRepository {
return await dbManager.transaction(async (db) => {
// Füge Gruppe hinzu
const groupResult = await db.run(`
INSERT INTO groups (group_id, year, title, description, name, upload_date)
VALUES (?, ?, ?, ?, ?, ?)
INSERT INTO groups (group_id, year, title, description, name, upload_date, approved)
VALUES (?, ?, ?, ?, ?, ?, ?)
`, [
groupData.groupId,
groupData.year,
groupData.title,
groupData.description || null,
groupData.name || null,
groupData.uploadDate
groupData.uploadDate,
groupData.approved || false
]);
// Füge Bilder hinzu

View File

@ -1,9 +1,10 @@
const generateId = require("shortid");
const express = require('express');
const { Router } = require('express');
const path = require('path');
const { endpoints } = require('../constants');
const UploadGroup = require('../models/uploadGroup');
const GroupRepository = require('../repositories/GroupRepository');
const groupRepository = require('../repositories/GroupRepository');
const dbManager = require('../database/DatabaseManager');
const ImagePreviewService = require('../services/ImagePreviewService');
@ -97,7 +98,7 @@ router.post(endpoints.UPLOAD_BATCH, async (req, res) => {
});
// Speichere Gruppe in SQLite
await GroupRepository.createGroup({
await groupRepository.createGroup({
groupId: group.groupId,
year: group.year,
title: group.title,

View File

@ -4,6 +4,8 @@ const { Router } = require('express');
const { endpoints, UPLOAD_FS_DIR, PREVIEW_FS_DIR } = require('../constants');
const path = require('path');
const ImagePreviewService = require('../services/ImagePreviewService');
const groupRepository = require('../repositories/GroupRepository');
const fs = require('fs');
const router = Router();
@ -20,6 +22,7 @@ router.post(endpoints.UPLOAD_FILE, async (req, res) => {
}
const file = req.files.file;
const groupName = req.body.groupName || 'Unnamed Group';
fileEnding = file.name.split(".")
fileEnding = fileEnding[fileEnding.length - 1]
@ -36,6 +39,10 @@ router.post(endpoints.UPLOAD_FILE, async (req, res) => {
});
});
// Get file stats
const fileStats = fs.statSync(savePath);
const fileSize = fileStats.size;
// Generate preview asynchronously (don't wait for it)
const previewFileName = ImagePreviewService._getPreviewFileName(fileName);
const previewPath = ImagePreviewService.getPreviewPath(previewFileName);
@ -50,15 +57,45 @@ router.post(endpoints.UPLOAD_FILE, async (req, res) => {
console.error(`Unexpected error during preview generation for ${fileName}:`, err);
});
// Create or update group in database
const groupId = generateId();
const currentYear = new Date().getFullYear();
const uploadDate = new Date().toISOString();
const groupData = {
groupId: groupId,
year: currentYear,
title: groupName,
description: `Einzelnes Bild Upload: ${file.name}`,
name: groupName,
uploadDate: uploadDate,
images: [{
fileName: fileName,
originalName: file.name,
filePath: `${endpoints.UPLOAD_STATIC_DIRECTORY}/${fileName}`,
uploadOrder: 1,
fileSize: fileSize,
mimeType: file.mimetype,
previewPath: `${endpoints.PREVIEW_STATIC_DIRECTORY}/${previewFileName}`
}]
};
// Save to database
await groupRepository.createGroup(groupData);
console.log(`✅ Group created: ${groupName} with image ${fileName}`);
// Return immediately with file path
res.json({
filePath: `${endpoints.UPLOAD_STATIC_DIRECTORY}/${fileName}`,
fileName: fileName
fileName: fileName,
groupId: groupId,
groupName: groupName
});
} catch(err) {
console.error(err);
return res.status(500).send(err);
console.error('Upload error:', err);
return res.status(500).json({ error: err.message });
}
});

View File

@ -23,6 +23,9 @@ RUN chmod +x ./env.sh
# Copy nginx configuration for development
COPY docker/dev/frontend/nginx.conf /etc/nginx/conf.d/default.conf
# Copy htpasswd file for authentication
COPY docker/dev/frontend/config/htpasswd /etc/nginx/.htpasswd
# Make /app owned by the non-root user, then run npm as that user so
# node_modules are created with the correct owner and we avoid an expensive
# recursive chown later.

View File

@ -0,0 +1 @@
admin:$apr1$q2zv8h0V$ueMqnKIeQU6NN1YnHWNVe/

View File

@ -1,121 +1,301 @@
server {
listen 80;
server_name localhost;
events {server {
worker_connections 1024; listen 80;
} server_name localhost;
client_max_body_size 200M;
# API proxy to development backend
# Upload endpoint
location /api/upload {
proxy_pass http://backend-dev: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;
http {
include /etc/nginx/mime.types; # API proxy to development backend
default_type application/octet-stream; # Upload endpoint
location /api/upload {
# Logging proxy_pass http://backend-dev:5000/upload/;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' proxy_set_header Host $host;
'$status $body_bytes_sent "$http_referer" ' proxy_set_header X-Real-IP $remote_addr;
'"$http_user_agent" "$http_x_forwarded_for"'; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
access_log /var/log/nginx/access.log main; client_max_body_size 100M;
error_log /var/log/nginx/error.log warn; }
# Gzip Settings # Download original images
gzip on; # Handle POST requests to upload endpoint
gzip_vary on; location /api/upload {
gzip_min_length 1024; proxy_pass http://backend-dev:5000/upload;
gzip_proxied any; proxy_set_header Host $host;
gzip_comp_level 6; proxy_set_header X-Real-IP $remote_addr;
gzip_types proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
text/plain proxy_set_header X-Forwarded-Proto $scheme;
text/css proxy_set_header Content-Type $content_type;
text/xml client_max_body_size 100M;
text/javascript }
application/json
application/javascript # Preview/thumbnail images (optimized for gallery views)
application/xml+rss location /api/download {
application/atom+xml proxy_pass http://backend-dev:5000/download;
image/svg+xml; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Server Config proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
server { proxy_set_header X-Forwarded-Proto $scheme;
listen 80; }
# Allow large uploads (50MB) location /api/previews {
client_max_body_size 50M; proxy_pass http://backend-dev:5000/previews;
proxy_set_header Host $host;
# API proxy to backend-dev service proxy_set_header X-Real-IP $remote_addr;
location /upload { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://backend-dev:5000; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host; }
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Add groups endpoint
proxy_set_header X-Forwarded-Proto $scheme; location /api/groups {
proxy_pass http://backend-dev:5000/groups;
# Allow large uploads for API too proxy_set_header Host $host;
client_max_body_size 50M; 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 routes for new multi-upload features }
location /api/upload {
proxy_pass http://backend-dev:5000/upload; # Groups API
proxy_set_header Host $host; location /api/groups {
proxy_set_header X-Real-IP $remote_addr; proxy_pass http://backend-dev:5000/groups;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Allow large uploads for batch upload proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M; }
}
# Moderation Groups API (PASSWORD PROTECTED)
# API - Download original images location /moderation/groups {
location /api/download { auth_basic "Restricted Area - Moderation API";
proxy_pass http://backend-dev:5000/download; auth_basic_user_file /etc/nginx/.htpasswd;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_pass http://backend-dev:5000/moderation/groups;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; 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
# Handle POST requests to upload endpoint
location /api/upload {
proxy_pass http://backend-dev: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;
# API - Preview/thumbnail images (optimized for gallery views) }
location /api/previews {
proxy_pass http://backend-dev:5000/previews; # Groups dynamic routes
proxy_set_header Host $host; location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ {
proxy_set_header X-Real-IP $remote_addr; proxy_pass http://backend-dev:5000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; 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_set_header Content-Type $content_type;
client_max_body_size 100M;
}
# Preview/thumbnail images (optimized for gallery views)
location /api/download {
proxy_pass http://backend-dev: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 - Groups (NO PASSWORD PROTECTION) }
location /api/previews {
proxy_pass http://backend-dev: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;
}
location /api/groups {
# Add groups endpoint
location /api/groups {
proxy_pass http://backend-dev: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;
}
proxy_pass http://backend-dev:5000/groups; # Legacy download endpoint (backwards compatibility)
# Groups API
location /api/groups {
proxy_pass http://backend-dev: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;
}
proxy_set_header Host $host; location /download {
# Moderation Groups API
location /moderation/groups {
proxy_pass http://backend-dev: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;
}
proxy_set_header X-Real-IP $remote_addr; proxy_pass http://backend-dev:5000/download;
# Groups dynamic routes
location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ {
proxy_pass http://backend-dev:5000;
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_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;
# Moderation routes
location /moderation {
proxy_pass http://backend-dev:5000;
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_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr;
# Legacy download endpoint (backwards compatibility)
location /download {
proxy_pass http://backend-dev: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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support for hot reloading (React Dev Server)
location /ws {
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;
}
proxy_set_header X-Forwarded-Proto $scheme;
# All other requests go to React Dev Server for Hot Module Reloading
location / {
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 API - Moderation API routes (password protected) - must come before /groups }
location /moderation/groups {
auth_basic "Restricted Area - Moderation API"; # WebSocket support for hot reloading (React Dev Server)
auth_basic_user_file /etc/nginx/.htpasswd; location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_pass http://backend-dev:5000/moderation/groups; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr;
} proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# API - Groups API routes (NO PASSWORD PROTECTION)
location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ { # Protected Frontend Routes - Moderation Pages (PASSWORD PROTECTED)
proxy_pass http://backend-dev:5000; location /moderation {
proxy_set_header Host $host; auth_basic "Restricted Area - Moderation";
proxy_set_header X-Real-IP $remote_addr; auth_basic_user_file /etc/nginx/.htpasswd;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://127.0.0.1:3000;
} proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
location /download { proxy_set_header Connection "Upgrade";
proxy_pass http://backend-dev:5000; proxy_set_header Host $host;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
proxy_set_header X-Forwarded-Proto $scheme;
} # All other requests go to React Dev Server for Hot Module Reloading
location / {
# Frontend page - Groups overview (NO PASSWORD PROTECTION) - React Dev Server proxy_pass http://127.0.0.1:3000;
location /groups { proxy_http_version 1.1;
proxy_pass http://127.0.0.1:3000; proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1; proxy_set_header Connection "Upgrade";
proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host;
proxy_set_header Connection "Upgrade"; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
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";
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;
}
# WebSocket support for hot reloading (React Dev Server)
location /ws {
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;
}
# All other requests go to React Dev Server for Hot Module Reloading
location / {
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;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

View File

@ -0,0 +1,129 @@
server {
listen 80;
server_name localhost;
client_max_body_size 200M;
# API proxy to development backend
# Upload endpoint
location /api/upload {
proxy_pass http://backend-dev: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
# Handle POST requests to upload endpoint
location /api/upload {
proxy_pass http://backend-dev: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;
proxy_set_header Content-Type $content_type;
client_max_body_size 100M;
}
# Preview/thumbnail images (optimized for gallery views)
location /api/download {
proxy_pass http://backend-dev: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;
}
location /api/previews {
proxy_pass http://backend-dev: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;
}
# Add groups endpoint
location /api/groups {
proxy_pass http://backend-dev: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;
}
# Groups API
location /api/groups {
proxy_pass http://backend-dev: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 Groups API (PASSWORD PROTECTED)
location /moderation/groups {
auth_basic "Restricted Area - Moderation API";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend-dev: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 dynamic routes
location ~ ^/groups/[a-zA-Z0-9_-]+(/.*)?$ {
proxy_pass http://backend-dev:5000;
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;
}
# Legacy download endpoint (backwards compatibility)
location /download {
proxy_pass http://backend-dev: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;
}
# WebSocket support for hot reloading (React Dev Server)
location /ws {
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 Frontend Routes - Moderation Pages (PASSWORD PROTECTED)
location /moderation {
auth_basic "Restricted Area - Moderation";
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;
}
# All other requests go to React Dev Server for Hot Module Reloading
location / {
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;
}
}