Task 12: ManagementPortalPage - Self-Service Portal Implementation - New page: ManagementPortalPage.js (~650 lines) with token-based auth - Maximum component reuse (ImageGalleryCard, ImageGallery, DescriptionInput, ConsentBadges) - Single-page layout without tabs (consistent with ModerationGroupImagesPage) - All CRUD operations: view, edit metadata, delete images, revoke/restore consents, delete group - Data transformation: API camelCase → Component snake_case (ConsentBadges compatibility) - Error handling: 404 invalid token, 429 rate-limit, general errors - Route added: /manage/:token in App.js Task 20: nginx Configuration for Management API - Dev: Proxy /api/manage/* → backend-dev:5000 - Prod: Proxy /api/manage/* → image-uploader-backend:5000 - Headers: Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto - Frontend container rebuilt with new nginx config Navigation Enhancement (Navbar.js): - Conditional rendering with useLocation() hook - Show "Upload" always (active only on /) - Show "Mein Upload" additionally on /manage/:token (active) - Both buttons visible simultaneously on management page Test Results: ✅ Token validation (404 on invalid) ✅ API routing through nginx ✅ ConsentBadges display correctly ✅ All CRUD operations functional ✅ Rate-limiting working (429 on excessive requests) ✅ Navigation highlighting correct ✅ Component reuse: 0 lines duplicated code Known Issues (to be fixed in separate bugfix session): ⚠️ Issue 6: ModerationGroupsPage - Filter "Alle Gruppen" not working ⚠️ Issue 7: Export button "Consent-Daten exportieren" not working Files Changed: - frontend/src/Components/Pages/ManagementPortalPage.js (NEW) - frontend/src/App.js (route added) - frontend/src/Components/ComponentUtils/Headers/Navbar.js (conditional nav) - docker/dev/frontend/nginx.conf (proxy config) - docker/prod/frontend/nginx.conf (proxy config) - docs/FEATURE_PLAN-social-media.md (documentation updated)
55 KiB
Feature Plan: Social Media Consent Management
📋 Übersicht
Feature: Einwilligungsverwaltung für Bildveröffentlichung in Werkstatt und Social Media
Ziel: Rechtskonforme Einholung und Verwaltung von Nutzerzustimmungen für die Anzeige von Bildern in der Werkstatt und Veröffentlichung auf Social Media Plattformen
Priorität: High (Rechtliche Anforderung)
Status: ✅ Phase 1 komplett (9-10. Nov 2025) | ✅ Phase 2 Backend komplett (11. Nov 2025)
API-Endpoints:
- ✅
GET /api/social-media/platforms- Liste aktiver Social Media Plattformen - ✅
POST /api/groups/:groupId/consents- Consents speichern - ✅
GET /api/groups/:groupId/consents- Consents abrufen - ✅
GET /api/admin/groups/by-consent- Gruppen nach Consent filtern - ✅
GET /api/admin/consents/export- Consent-Daten exportieren (CSV/JSON)
Test-Ergebnisse (10. Nov 2025):
- ✅ Upload mit Consent: Funktioniert
- ✅ Upload ohne Werkstatt-Consent: Blockiert (400 Error)
- ✅ Filter "Alle Gruppen": 76 Gruppen
- ✅ Filter "Nur Werkstatt": 74 Gruppen
- ✅ Filter "Facebook": 2 Gruppen
- ✅ Export-Button: CSV-Download funktioniert
- ✅ ConsentBadges: Icons und Tooltips werden korrekt angezeigt
- ✅ Automatische Migration: Migration 005 & 006 beim Backend-Start angewendet
- ✅ GDPR-Konformität: 72 alte Gruppen mit display_in_workshop = 0
- ✅ Social Media Plattformen: 3 Plattformen (Facebook, Instagram, TikTok)
Phase 2 Backend (11. Nov 2025)
Implementierungszeit: Phase 1: 2 Tage | Phase 2 Backend: 1 Tag
🎯 Funktionale Anforderungen
Must-Have (Phase 1)
- Pflicht-Zustimmung: Nutzer muss der Anzeige in der Werkstatt zustimmen (ohne Zustimmung kein Upload möglich)
- Social Media Zustimmung: Optionale, separate Zustimmungen für verschiedene Social Media Plattformen (Facebook, Instagram, TikTok)
- Aufklärungstext: Informativer Text über Prüfung und Freigabe durch Hobbyhimmel
- Datenbankpersistierung: Speicherung der Zustimmungen pro Upload-Gruppe mit Zeitstempel
- Moderation-Filter: Filter im Moderation Panel nach Consent-Status
- Visuelle Kennzeichnung: Icons/Tags für Social Media Freigaben im Moderation Panel
- Export-Funktion: Möglichkeit zum Export der Consent-Daten für rechtliche Dokumentation
- Gruppen-ID Anzeige: Nach Upload wird Gruppen-ID als Referenz angezeigt
- Widerrufs-Information: Hinweis auf Kontaktmöglichkeit für Widerruf der Zustimmung
Nice-to-Have (Phase 2) - ✅ Backend 100% KOMPLETT (11. Nov 2025)
- Management-Token-System: UUID v4 Token-Generation bei Upload
- Token-Validierung API: GET /api/manage/:token (200 mit Gruppendaten oder 404)
- Consent-Widerruf API: PUT /api/manage/:token/consents (Workshop & Social Media)
- Metadata-Edit API: PUT /api/manage/:token/metadata (Titel & Beschreibung editieren)
- Bilder hinzufügen API: POST /api/manage/:token/images (max 50 Bilder pro Gruppe)
- Bild löschen API: DELETE /api/manage/:token/images/:imageId (verhindert letztes Bild)
- Gruppe löschen API: DELETE /api/manage/:token (komplette Gruppe inkl. Dateien)
- Rate-Limiting: IP-basiert 10 req/h, Brute-Force-Schutz 20 Versuche → 24h Block
- Management Audit-Log: Migration 007, vollständige Historie aller Management-Aktionen
- Widerruf-Verhalten: Workshop setzt display_in_workshop=0, Social Media setzt revoked=1
- Frontend Management-Portal: React-Komponente /manage/:token (Tasks 12-17) ⏳
- E-Mail-Benachrichtigung: Optional E-Mail mit Verwaltungslink nach Upload ⏳
- Consent-Historie: Vollständige Audit-Trail aller Consent-Änderungen ⏳
🔒 Rechtliche Überlegungen
DSGVO-Konformität
- Eindeutige Einwilligung: Jede Zustimmung muss explizit durch Checkbox erfolgen (kein Pre-Check)
- Informierte Einwilligung: Klare Beschreibung, wofür die Daten verwendet werden
- Widerrufbarkeit: Nutzer muss Zustimmung jederzeit widerrufen können
- Dokumentation: Zeitstempel und Umfang der Einwilligung müssen gespeichert werden
- Zweckbindung: Separate Zustimmungen für verschiedene Verwendungszwecke (Werkstatt vs. Social Media)
Empfohlene Texte
Werkstatt-Anzeige (Pflicht)
Ich willige ein, dass meine hochgeladenen Bilder auf dem Monitor in der offenen
Werkstatt des Hobbyhimmels angezeigt werden dürfen. Die Bilder sind nur lokal
im Hobbyhimmel sichtbar und werden nicht über das Internet zugänglich gemacht.
Social Media Veröffentlichung (Optional)
Ich willige ein, dass meine Bilder und Texte auf folgenden Social Media Plattformen
veröffentlicht werden dürfen:
☐ Facebook
☐ Instagram
☐ TikTok
☐ [Weitere Plattformen...]
Die Veröffentlichung erfolgt inklusive aller von mir angegebenen Informationen
(Name, Beschreibung, etc.).
Aufklärungshinweis
ℹ️ Wichtiger Hinweis:
Alle hochgeladenen Inhalte werden vom Hobbyhimmel-Team geprüft, bevor sie angezeigt
oder veröffentlicht werden. Wir behalten uns vor, Inhalte nicht zu zeigen oder
rechtswidrige Inhalte zu entfernen. Nach dem Upload erhalten Sie eine Gruppen-ID
als Referenz.
Widerruf Ihrer Einwilligung:
Sie können Ihre Einwilligung jederzeit widerrufen. Bitte kontaktieren Sie uns hierzu
mit Ihrer Gruppen-ID unter: it@hobbyhimmel.de
🔧 Technische Umsetzung
Datenbankschema
Migration 1: Erweitere groups Tabelle
Datei: backend/src/database/migrations/005_add_consent_fields.sql
-- Migration 005: Add consent management fields
-- Füge Consent-Felder zur groups-Tabelle hinzu
ALTER TABLE groups ADD COLUMN display_in_workshop BOOLEAN NOT NULL DEFAULT 0;
ALTER TABLE groups ADD COLUMN consent_timestamp DATETIME;
ALTER TABLE groups ADD COLUMN management_token TEXT UNIQUE; -- Für Phase 2
-- Index für schnelle Abfragen
CREATE INDEX IF NOT EXISTS idx_groups_display_consent ON groups(display_in_workshop);
CREATE UNIQUE INDEX IF NOT EXISTS idx_groups_management_token ON groups(management_token) WHERE management_token IS NOT NULL;
-- ⚠️ WICHTIG - GDPR-KONFORM (Gefixt am 10. Nov 2025):
-- Alte Gruppen (vor dieser Migration) werden NICHT automatisch auf display_in_workshop = 1 gesetzt!
-- Sie haben nie explizit Consent gegeben und müssen bei display_in_workshop = 0 bleiben.
-- Nur NEUE Uploads (nach dieser Migration) bekommen Consent durch explizite Checkbox-Zustimmung.
Migration 2: Neue social_media_platforms Tabelle
Datei: backend/src/database/migrations/006_create_social_media_tables.sql
-- Migration 006: Social Media Platform Configuration und Consents
-- Tabelle für konfigurierbare Social Media Plattformen
CREATE TABLE IF NOT EXISTS social_media_platforms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform_name TEXT UNIQUE NOT NULL, -- z.B. 'facebook', 'instagram', 'tiktok'
display_name TEXT NOT NULL, -- z.B. 'Facebook', 'Instagram', 'TikTok'
icon_name TEXT, -- Optional: Material-UI Icon Name
is_active BOOLEAN DEFAULT 1, -- Plattform aktiv/inaktiv
sort_order INTEGER DEFAULT 0, -- Anzeigereihenfolge
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Tabelle für Consent pro Gruppe und Plattform
CREATE TABLE IF NOT EXISTS group_social_media_consents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id TEXT NOT NULL,
platform_id INTEGER NOT NULL,
consented BOOLEAN NOT NULL DEFAULT 0,
consent_timestamp DATETIME NOT NULL,
revoked BOOLEAN DEFAULT 0, -- Für Widerruf (Phase 2)
revoked_timestamp DATETIME, -- Wann widerrufen (Phase 2)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (group_id) REFERENCES groups(group_id) ON DELETE CASCADE,
FOREIGN KEY (platform_id) REFERENCES social_media_platforms(id) ON DELETE CASCADE,
UNIQUE(group_id, platform_id) -- Jede Plattform nur einmal pro Gruppe
);
-- Indizes
CREATE INDEX IF NOT EXISTS idx_consents_group_id ON group_social_media_consents(group_id);
CREATE INDEX IF NOT EXISTS idx_consents_platform_id ON group_social_media_consents(platform_id);
CREATE INDEX IF NOT EXISTS idx_consents_consented ON group_social_media_consents(consented);
-- Trigger für updated_at
CREATE TRIGGER IF NOT EXISTS update_consents_timestamp
AFTER UPDATE ON group_social_media_consents
FOR EACH ROW
BEGIN
UPDATE group_social_media_consents SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
END;
-- Standard-Plattformen einfügen
INSERT INTO social_media_platforms (platform_name, display_name, icon_name, sort_order) VALUES
('facebook', 'Facebook', 'Facebook', 1),
('instagram', 'Instagram', 'Instagram', 2),
('tiktok', 'TikTok', 'MusicNote', 3);
Backend-Erweiterungen
1. Repository-Erweiterungen
Datei: backend/src/repositories/GroupRepository.js
Neue Methoden:
// Consent Management
async createGroupWithConsent(groupData, workshopConsent, socialMediaConsents)
async getGroupWithConsents(groupId)
async updateConsents(groupId, socialMediaConsents)
async getGroupsByConsentStatus(displayInWorkshop, platformName = null)
// Social Media Platforms
async getActiveSocialMediaPlatforms()
async getSocialMediaConsentsForGroup(groupId)
// Export
async exportConsentData(filters = {})
// Management Token (Phase 2)
async generateManagementToken(groupId)
async getGroupByManagementToken(token)
2. Neue Repository-Klasse
Datei: backend/src/repositories/SocialMediaRepository.js
class SocialMediaRepository {
constructor(dbManager) {
this.db = dbManager;
}
// Platform Management
async getAllPlatforms()
async getActivePlatforms()
async createPlatform(platformData)
async updatePlatform(platformId, platformData)
async togglePlatformStatus(platformId, isActive)
// Consent Management
async saveConsents(groupId, consentsArray)
async getConsentsForGroup(groupId)
async revokeConsent(groupId, platformId) // Phase 2
async getConsentHistory(groupId) // Phase 2
}
3. API-Routes
Datei: backend/src/routes/consent.js (NEU)
// GET /api/social-media/platforms - Liste aller aktiven Plattformen
router.get('/social-media/platforms', async (req, res) => {
// Returns: [{ id, platform_name, display_name, icon_name, sort_order }]
});
// POST /api/groups/:groupId/consents - Speichere Consents für Gruppe
router.post('/groups/:groupId/consents', async (req, res) => {
// Body: { workshopConsent: true, socialMediaConsents: [{ platformId: 1, consented: true }] }
});
// GET /api/groups/:groupId/consents - Lade Consents für Gruppe
router.get('/groups/:groupId/consents', async (req, res) => {
// Returns: { workshopConsent, socialMediaConsents, consentTimestamp }
});
// GET /api/admin/groups/by-consent - Filter Gruppen nach Consent (Admin)
router.get('/admin/groups/by-consent', async (req, res) => {
// Query params: ?workshopConsent=true&platform=facebook
});
// GET /api/admin/consents/export - Export Consent-Daten (Admin)
router.get('/admin/consents/export', async (req, res) => {
// Returns CSV or JSON with all consent data
});
Datei: backend/src/routes/management.js (NEU - Phase 2)
// GET /api/manage/:token - Validiere Management-Token und lade Gruppe
router.get('/manage/:token', async (req, res) => {});
// PUT /api/manage/:token/consents - Widerrufe/Ändere Consents
router.put('/manage/:token/consents', async (req, res) => {});
// DELETE /api/manage/:token/images/:imageId - Lösche Bild
router.delete('/manage/:token/images/:imageId', async (req, res) => {});
4. Upload-Route Anpassung
Datei: backend/src/routes/batchUpload.js
// Erweitere Upload-Logik um Consent-Speicherung
router.post('/upload-batch', upload.array('images', 50), async (req, res) => {
try {
// ... existing upload logic ...
// Parse consent data
const { workshopConsent, socialMediaConsents } = JSON.parse(req.body.consents || '{}');
// Validate workshop consent (required)
if (!workshopConsent) {
return res.status(400).json({
error: 'Workshop display consent is required'
});
}
// Save group with consents
const groupId = await groupRepository.createGroupWithConsent(
groupData,
workshopConsent,
socialMediaConsents
);
// Generate management token (Phase 2)
const managementToken = await groupRepository.generateManagementToken(groupId);
res.json({
success: true,
groupId,
managementToken, // Phase 2
message: 'Upload successful'
});
} catch (error) {
// Error handling
}
});
Frontend-Komponenten
1. Neue Komponente: ConsentCheckboxes
Datei: frontend/src/Components/ComponentUtils/MultiUpload/ConsentCheckboxes.js
import React, { useState, useEffect } from 'react';
import {
Box,
FormControlLabel,
Checkbox,
Typography,
Paper,
Divider,
Alert
} from '@mui/material';
import InfoIcon from '@mui/icons-material/Info';
import FacebookIcon from '@mui/icons-material/Facebook';
import InstagramIcon from '@mui/icons-material/Instagram';
import MusicNoteIcon from '@mui/icons-material/MusicNote';
const ICON_MAP = {
'Facebook': FacebookIcon,
'Instagram': InstagramIcon,
'MusicNote': MusicNoteIcon,
};
function ConsentCheckboxes({ onConsentChange, consents, disabled = false }) {
const [platforms, setPlatforms] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Lade verfügbare Plattformen vom Backend
fetchPlatforms();
}, []);
const fetchPlatforms = async () => {
try {
const response = await fetch('/api/social-media/platforms');
const data = await response.json();
setPlatforms(data);
} catch (error) {
console.error('Error loading platforms:', error);
} finally {
setLoading(false);
}
};
const handleWorkshopChange = (event) => {
onConsentChange({
...consents,
workshopConsent: event.target.checked
});
};
const handleSocialMediaChange = (platformId) => (event) => {
const updatedConsents = { ...consents };
const platformConsents = updatedConsents.socialMediaConsents || [];
if (event.target.checked) {
platformConsents.push({ platformId, consented: true });
} else {
const index = platformConsents.findIndex(c => c.platformId === platformId);
if (index > -1) platformConsents.splice(index, 1);
}
updatedConsents.socialMediaConsents = platformConsents;
onConsentChange(updatedConsents);
};
const isPlatformChecked = (platformId) => {
return consents.socialMediaConsents?.some(c => c.platformId === platformId) || false;
};
return (
<Paper sx={{ p: 3, mb: 3, borderRadius: '12px', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }}>
{/* Aufklärungshinweis */}
<Alert severity="info" icon={<InfoIcon />} sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
Wichtiger Hinweis
</Typography>
<Typography variant="body2">
Alle hochgeladenen Inhalte werden vom Hobbyhimmel-Team geprüft, bevor sie
angezeigt oder veröffentlicht werden. Wir behalten uns vor, Inhalte nicht
zu zeigen oder rechtswidrige Inhalte zu entfernen.
</Typography>
<Typography variant="body2" sx={{ mt: 1 }}>
Nach dem Upload erhalten Sie eine Gruppen-ID als Referenz für die Kontaktaufnahme.
</Typography>
</Alert>
{/* Pflicht-Zustimmung: Werkstatt-Anzeige */}
<Box sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600, color: '#333' }}>
Anzeige in der Werkstatt *
</Typography>
<FormControlLabel
control={
<Checkbox
checked={consents.workshopConsent || false}
onChange={handleWorkshopChange}
disabled={disabled}
required
sx={{
color: '#4CAF50',
'&.Mui-checked': { color: '#4CAF50' }
}}
/>
}
label={
<Typography variant="body2" sx={{ color: '#555' }}>
Ich willige ein, dass meine hochgeladenen Bilder auf dem Monitor in
der offenen Werkstatt des Hobbyhimmels angezeigt werden dürfen.
Die Bilder sind nur lokal im Hobbyhimmel sichtbar und werden nicht
über das Internet zugänglich gemacht. <strong>(Pflichtfeld)</strong>
</Typography>
}
/>
</Box>
<Divider sx={{ my: 3 }} />
{/* Optional: Social Media Veröffentlichung */}
<Box>
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600, color: '#333' }}>
Social Media Veröffentlichung (optional)
</Typography>
<Typography variant="body2" sx={{ mb: 2, color: '#666' }}>
Ich willige ein, dass meine Bilder und Texte auf folgenden Social Media
Plattformen veröffentlicht werden dürfen (inklusive aller angegebenen
Informationen wie Name und Beschreibung):
</Typography>
{loading ? (
<Typography>Lade Plattformen...</Typography>
) : (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{platforms.map(platform => {
const IconComponent = ICON_MAP[platform.icon_name] || InfoIcon;
return (
<FormControlLabel
key={platform.id}
control={
<Checkbox
checked={isPlatformChecked(platform.id)}
onChange={handleSocialMediaChange(platform.id)}
disabled={disabled}
sx={{
color: '#2196F3',
'&.Mui-checked': { color: '#2196F3' }
}}
/>
}
label={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<IconComponent fontSize="small" />
<Typography variant="body2">
{platform.display_name}
</Typography>
</Box>
}
/>
);
})}
</Box>
)}
</Box>
{/* Widerrufs-Hinweis */}
<Alert severity="info" sx={{ mt: 3 }}>
<Typography variant="caption" sx={{ display: 'block' }}>
<strong>Widerruf Ihrer Einwilligung:</strong> Sie können Ihre Einwilligung
jederzeit widerrufen. Kontaktieren Sie uns hierzu mit Ihrer Gruppen-ID unter:
<strong> it@hobbyhimmel.de</strong>
</Typography>
</Alert>
</Paper>
);
}
export default ConsentCheckboxes;
2. Neue Komponente: UploadSuccessDialog
Datei: frontend/src/Components/ComponentUtils/MultiUpload/UploadSuccessDialog.js
import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Typography,
Box,
IconButton,
Alert,
TextField,
InputAdornment
} from '@mui/material';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
function UploadSuccessDialog({ open, onClose, uploadResult }) {
const [copied, setCopied] = useState(false);
const handleCopyGroupId = () => {
navigator.clipboard.writeText(uploadResult.groupId);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const handleCopyManagementLink = () => {
const link = `${window.location.origin}/manage/${uploadResult.managementToken}`;
navigator.clipboard.writeText(link);
};
return (
<Dialog
open={open}
onClose={onClose}
maxWidth="sm"
fullWidth
PaperProps={{
sx: { borderRadius: '12px', p: 2 }
}}
>
<DialogTitle sx={{ textAlign: 'center', pb: 1 }}>
<CheckCircleIcon sx={{ fontSize: 60, color: '#4CAF50', mb: 1 }} />
<Typography variant="h5" sx={{ fontWeight: 600 }}>
Upload erfolgreich!
</Typography>
</DialogTitle>
<DialogContent>
<Typography variant="body1" sx={{ mb: 3, textAlign: 'center', color: '#666' }}>
{uploadResult.imageCount} Bild{uploadResult.imageCount !== 1 ? 'er' : ''} wurden erfolgreich hochgeladen.
</Typography>
<Alert severity="success" sx={{ mb: 3 }}>
<Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
Ihre Gruppen-ID (Referenznummer):
</Typography>
<TextField
fullWidth
value={uploadResult.groupId}
InputProps={{
readOnly: true,
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleCopyGroupId} edge="end">
<ContentCopyIcon />
</IconButton>
</InputAdornment>
),
}}
sx={{
'& .MuiOutlinedInput-root': {
fontFamily: 'monospace',
fontSize: '14px'
}
}}
/>
{copied && (
<Typography variant="caption" sx={{ color: '#4CAF50', mt: 0.5, display: 'block' }}>
✓ In Zwischenablage kopiert
</Typography>
)}
</Alert>
{/* Phase 2: Management Link */}
{uploadResult.managementToken && (
<Alert severity="info" sx={{ mb: 2 }}>
<Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
Verwaltungslink (nur für Sie):
</Typography>
<Typography variant="caption" sx={{ display: 'block', mb: 1 }}>
Über diesen Link können Sie Ihre Bilder verwalten, Beschreibungen ändern
oder Ihre Einwilligung widerrufen. Speichern Sie diesen Link sicher!
</Typography>
<Button
size="small"
startIcon={<ContentCopyIcon />}
onClick={handleCopyManagementLink}
sx={{ textTransform: 'none' }}
>
Link kopieren
</Button>
</Alert>
)}
<Alert severity="info">
<Typography variant="body2">
Ihre Bilder werden vom Hobbyhimmel-Team geprüft und dann freigegeben.
Bei Fragen oder zum Widerruf Ihrer Einwilligung kontaktieren Sie uns
bitte mit Ihrer Gruppen-ID unter: <strong>it@hobbyhimmel.de</strong>
</Typography>
</Alert>
</DialogContent>
<DialogActions sx={{ justifyContent: 'center', pt: 2 }}>
<Button
onClick={onClose}
variant="contained"
size="large"
sx={{
borderRadius: '25px',
px: 4,
py: 1.5,
background: 'linear-gradient(45deg, #4CAF50 30%, #45a049 90%)',
'&:hover': {
background: 'linear-gradient(45deg, #45a049 30%, #4CAF50 90%)',
}
}}
>
Alles klar!
</Button>
</DialogActions>
</Dialog>
);
}
export default UploadSuccessDialog;
3. MultiUploadPage Anpassung
Datei: frontend/src/Components/Pages/MultiUploadPage.js
Änderungen:
- Import ConsentCheckboxes und UploadSuccessDialog
- State für Consents hinzufügen
- Upload-Button nur aktivieren wenn workshopConsent = true
- Consents beim Upload mit senden
- UploadSuccessDialog nach erfolgreichem Upload anzeigen
// Neuer State
const [consents, setConsents] = useState({
workshopConsent: false,
socialMediaConsents: []
});
// Upload-Validierung erweitern
const handleUpload = async () => {
// ... existing checks ...
if (!consents.workshopConsent) {
Swal.fire({
icon: 'warning',
title: 'Zustimmung erforderlich',
text: 'Bitte stimmen Sie der Anzeige in der Werkstatt zu, um fortfahren zu können.',
confirmButtonColor: '#4CAF50'
});
return;
}
// Upload mit Consents
const result = await uploadImageBatch(filesToUpload, metadata, descriptionsArray, consents);
};
// Render mit ConsentCheckboxes
<ConsentCheckboxes
consents={consents}
onConsentChange={setConsents}
disabled={uploading}
/>
4. Moderation Panel Erweiterungen
Datei: frontend/src/Components/Pages/ModerationGroupsPage.js
Neue Features:
- Filter-Dropdown für Consent-Status (Alle / Nur Werkstatt / Facebook / Instagram / TikTok)
- Social Media Icons als Badges bei jeder Gruppe
- Export-Button für Consent-Daten
- Consent-Details in Gruppen-Ansicht
// Neue Komponente: ConsentBadges
function ConsentBadges({ consents }) {
return (
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
{consents.socialMediaConsents?.map(consent => (
<Chip
key={consent.platformId}
icon={<PlatformIcon name={consent.platformName} />}
label={consent.platformDisplayName}
size="small"
color="primary"
variant="outlined"
/>
))}
</Box>
);
}
Utils-Erweiterung
Upload Utility
Datei: frontend/src/Utils/batchUpload.js
export const uploadImageBatch = async (files, metadata, descriptions, consents) => {
const formData = new FormData();
// ... existing file append logic ...
// Add consent data
formData.append('consents', JSON.stringify({
workshopConsent: consents.workshopConsent,
socialMediaConsents: consents.socialMediaConsents
}));
const response = await fetch('/api/upload-batch', {
method: 'POST',
body: formData
});
const result = await response.json();
return result;
};
📝 Implementierungs-Aufgaben
Phase 1: Core Consent Management (Must-Have)
Backend Tasks
Task 1.1: Datenbank-Migrationen ⏱️ 1-2h ✅ KOMPLETT ERLEDIGT
- Migration 005:
display_in_workshop,consent_timestamp,management_tokenzugroupshinzufügen - Migration 006:
social_media_platformsundgroup_social_media_consentsTabellen erstellen - Standard-Plattformen (Facebook, Instagram, TikTok) einfügen
- ✅ Automatisches Migrationssystem gefixt (DatabaseManager entfernt jetzt inline Kommentare korrekt)
- ✅ GDPR-Fix getestet: Alle 72 Produktionsgruppen haben display_in_workshop = 0
Task 1.2: Repository-Erweiterungen ⏱️ 3-4h ✅ ERLEDIGT
GroupRepository:createGroupWithConsent()implementierenGroupRepository:getGroupWithConsents()implementierenGroupRepository:getGroupsByConsentStatus()für Moderation-FilterSocialMediaRepository: Neue Klasse erstellenSocialMediaRepository: Platform-Management-MethodenSocialMediaRepository: Consent-Management-Methoden- Unit-Tests für neue Repository-Methoden (TODO: später)
Task 1.3: API-Routes ⏱️ 3-4h ✅ ERLEDIGT
- Route
GET /api/social-media/platformserstellen - Route
POST /api/groups/:groupId/consentserstellen - Route
GET /api/groups/:groupId/consentserstellen - Route
GET /api/admin/groups/by-consentfür Moderation-Filter - Route
GET /api/admin/consents/exportfür CSV/JSON Export - Validierung und Error-Handling
- Integration-Tests für Routes (TODO: später)
Task 1.4: Upload-Route Anpassung ⏱️ 2h ✅ ERLEDIGT
batchUpload.js: Consent-Parameter entgegennehmen- Validierung:
workshopConsentmuss true sein - Consent-Daten mit Gruppe speichern
- Timestamp setzen
- Response um
groupIderweitern - Error-Handling bei fehlender Zustimmung
Frontend Tasks
Task 1.5: ConsentCheckboxes Komponente ⏱️ 4-5h ✅ ERLEDIGT
- Komponente erstellen mit Material-UI
- Aufklärungstext-Alert implementieren
- Pflicht-Checkbox für Werkstatt-Anzeige
- Dynamische Plattform-Liste vom Backend laden
- Social Media Checkboxen generieren
- Icon-Mapping für Plattformen
- Widerrufs-Hinweis anzeigen
- Responsive Design
- Props für Disabled-State und onChange-Callback
Task 1.6: UploadSuccessDialog Komponente ⏱️ 2-3h ✅ ERLEDIGT (als inline Content)
- Success-Content mit Gruppen-ID prominent anzeigen
- Aufklärungstext über Prüfung anzeigen
- Kontakt-Information einbinden
- Responsive Design
- Animation für Success-State
- Inline statt Dialog (User-Request)
Task 1.7: MultiUploadPage Integration ⏱️ 2-3h ✅ ERLEDIGT
- State für Consents hinzufügen
- ConsentCheckboxes einbinden (nach DescriptionInput - User-Request)
- Upload-Button nur aktivieren wenn
workshopConsent = true - Consents-Validation in
handleUpload() - Consents an Backend senden
- Success-Content nach Upload anzeigen (inline)
- Gruppen-ID aus Response verarbeiten
- Error-Handling für fehlende Zustimmung
Task 1.8: Moderation Panel - Consent-Anzeige ⏱️ 3-4h ✅ ERLEDIGT
- ConsentBadges Komponente erstellen
- Social Media Icons/Chips anzeigen
- Badges in Gruppen-Liste integrieren
- Consent-Details in Detailansicht
- Tooltip mit Consent-Timestamp
- Visuelle Unterscheidung (Werkstatt-only vs. Social Media)
Task 1.9: Moderation Panel - Filter & Export ⏱️ 3-4h ✅ ERLEDIGT
- Filter-Dropdown für Consent-Status
- API-Abfrage mit Filter-Parametern
- Export-Button implementieren
- CSV/JSON Export-Logik
- Download-Funktionalität
- Filter-State in URL (für Bookmarking) - Optional für später
Testing & Documentation
Task 1.10: Tests ⏱️ 3-4h ⏳ TODO
- Backend Unit-Tests für Repositories
- Backend Integration-Tests für API-Routes
- Frontend Component-Tests für ConsentCheckboxes
- Frontend Integration-Tests für Upload-Flow
- E2E-Test: Kompletter Upload mit Consents
- E2E-Test: Moderation mit Consent-Filter
Task 1.11: Dokumentation ⏱️ 2h ✅ ERLEDIGT
- README.md aktualisieren (neue Features)
- API-Dokumentation für neue Endpoints
- Datenbank-Schema dokumentieren
- FEATURE_PLAN aktualisiert mit Implementierungsstatus
- Screenshots für Consent-UI - Optional für später
- Deployment-Guide für Migrationen - Optional für später
Phase 2: Self-Service Management Portal (Nice-to-Have)
Backend Tasks
Task 2.1: Management-Token System ⏱️ 3-4h
- UUID-Token-Generierung implementieren
management_tokenin Gruppe speichern- Token-Validierungs-Logik
- Token-Expiration (optional, z.B. 90 Tage)
- Security: Rate-Limiting für Token-Zugriffe
Task 2.2: Management API-Routes ⏱️ 4-5h
- Route
GET /api/manage/:token- Token validieren und Gruppe laden - Route
PUT /api/manage/:token/consents- Consents widerrufen/ändern - Route
PUT /api/manage/:token/metadata- Titel/Beschreibung ändern - Route
DELETE /api/manage/:token/images/:imageId- Bild löschen - Route
DELETE /api/manage/:token- Gesamte Gruppe löschen - Audit-Log für alle Änderungen über Management-Portal
Task 2.3: Consent-Widerruf Logik ⏱️ 2-3h
revokedundrevoked_timestampin DB setzen- Consent-Historie für Audit-Trail
- Benachrichtigung an Admins bei Widerruf
- Automatische Entfernung von Social Media bei Widerruf
Frontend Tasks
Task 2.4: Management Portal Page ⏱️ 6-8h
- Neue Route
/manage/:tokenerstellen - Token-Validierung und Gruppe laden
- UI für Consent-Management
- UI für Metadaten-Bearbeitung
- UI für Bild-Löschung
- UI für Gruppen-Löschung
- Sicherheits-Bestätigungen (z.B. für Widerruf)
- Error-Handling bei ungültigem Token
Task 2.5: Management-Link in UploadSuccessDialog ⏱️ 1h
- Management-Link anzeigen
- Copy-to-Clipboard Funktionalität
- Hinweis zur sicheren Aufbewahrung
- Link-Vorschau mit Icon
Task 2.6: E-Mail-Benachrichtigung (optional) ⏱️ 4-6h
- Backend: E-Mail-Service integrieren (z.B. nodemailer)
- E-Mail-Template für Upload-Bestätigung
- Management-Link in E-Mail einbetten
- Optional: E-Mail-Adresse beim Upload abfragen
- DSGVO-Hinweis für E-Mail-Speicherung
🧪 Test-Szenarien
Acceptance Tests - Phase 1
-
Upload mit Pflicht-Zustimmung
- Upload ohne Werkstatt-Zustimmung wird blockiert
- Upload mit Werkstatt-Zustimmung funktioniert
- Consent-Timestamp wird korrekt gespeichert
-
Social Media Consents
- Keine Social Media Zustimmung: Upload erfolgreich, nur Werkstatt-Anzeige
- Eine Plattform: Consent wird korrekt gespeichert
- Mehrere Plattformen: Alle Consents werden gespeichert
- Plattform-Liste wird dynamisch geladen
-
Upload-Success Dialog
- Gruppen-ID wird angezeigt
- Copy-to-Clipboard funktioniert
- Informationstexte sind korrekt
-
Moderation Panel
- Social Media Badges werden angezeigt
- Filter nach Consent-Status funktioniert
- Export-Funktion liefert korrekten CSV/JSON
- Consent-Details sind sichtbar
Acceptance Tests - Phase 2
-
Management Portal
- Token-Zugriff funktioniert
- Consent-Widerruf funktioniert
- Metadaten können geändert werden
- Bilder können gelöscht werden
- Ungültiger Token wird abgelehnt
-
Sicherheit
- Token ist kryptisch und nicht erratbar (UUID v4)
- Rate-Limiting verhindert Token-Brute-Force
- Audit-Log für alle Änderungen vorhanden
📊 Datenbank-Beispiele
Gruppe mit Consents
-- groups Tabelle
INSERT INTO groups (group_id, year, title, name, display_in_workshop, consent_timestamp, management_token)
VALUES ('abc123', 2025, 'Mein Projekt', 'Max Mustermann', 1, '2025-11-09 14:30:00', 'a7f3k9m2-d4e8-4b1c-9f7a-3e2d8c1b5a6f');
-- group_social_media_consents Tabelle
INSERT INTO group_social_media_consents (group_id, platform_id, consented, consent_timestamp) VALUES
('abc123', 1, 1, '2025-11-09 14:30:00'), -- Facebook: Ja
('abc123', 2, 1, '2025-11-09 14:30:00'), -- Instagram: Ja
('abc123', 3, 0, '2025-11-09 14:30:00'); -- TikTok: Nein
Export-Format (CSV)
group_id,title,name,upload_date,workshop_consent,consent_timestamp,facebook,instagram,tiktok
abc123,Mein Projekt,Max Mustermann,2025-11-09 14:30:00,true,2025-11-09 14:30:00,true,true,false
def456,Anderes Projekt,Anna Schmidt,2025-11-10 10:15:00,true,2025-11-10 10:15:00,false,true,false
🚀 Deployment-Hinweise
Datenbank-Migration
# Backup vor Migration
sqlite3 backend/src/data/db/image_uploader.db ".backup backup-pre-consent.db"
# Migrationen ausführen
node backend/src/database/runMigrations.js
# Verifizierung
sqlite3 backend/src/data/db/image_uploader.db "SELECT * FROM social_media_platforms;"
Umgebungsvariablen (Phase 2)
# E-Mail Konfiguration (optional)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@hobbyhimmel.de
SMTP_PASS=your-password
# Management Token Expiration (in Tagen)
MANAGEMENT_TOKEN_EXPIRY=90
✅ Definition of Done
Phase 1
- Alle Backend-Migrationen erfolgreich durchgeführt
- Alle Backend-Routes implementiert und getestet
- Alle Frontend-Komponenten implementiert und integriert
- Upload funktioniert nur mit Werkstatt-Zustimmung
- Social Media Consents werden korrekt gespeichert
- Moderation Panel zeigt Consent-Status an
- Export-Funktion funktioniert
- Alle Tests grün
✅ Definition of Done
Phase 1 - ✅ 100% KOMPLETT ERLEDIGT (9-10. Nov 2025)
- Alle Backend-Migrationen erfolgreich durchgeführt (automatisch via DatabaseManager)
- Alle Backend-Routes implementiert und getestet
- Alle Frontend-Komponenten implementiert und integriert
- Upload funktioniert nur mit Werkstatt-Zustimmung
- Social Media Consents werden korrekt gespeichert
- Moderation Panel zeigt Consent-Status an
- Export-Funktion funktioniert
- Consent-Filter getestet (Alle: 76, Workshop-only: 74, Facebook: 2)
- Dokumentation aktualisiert
- ✅ Automatisches Migrationssystem gefixt (inline Kommentare werden entfernt)
- ✅ GDPR-Fix validiert: 72 alte Gruppen haben display_in_workshop = 0, 0 mit automatischem Consent
- ✅ Migration 005 & 006 laufen automatisch beim Backend-Start
- Code-Review durchgeführt (TODO: später)
- Deployment auf Production (bereit nach Code-Review)
Phase 2 - ✅ Backend 100% KOMPLETT (11. Nov 2025) | ⏳ Frontend ausstehend
Backend (Tasks 2-11):
- Management-Token-System implementiert (UUID v4)
- Token-Validierung API (GET /api/manage/:token)
- Consent-Widerruf API (PUT /api/manage/:token/consents)
- Metadata-Edit API (PUT /api/manage/:token/metadata)
- Bilder hinzufügen API (POST /api/manage/:token/images)
- Bild löschen API (DELETE /api/manage/:token/images/:imageId)
- Gruppe löschen API (DELETE /api/manage/:token)
- Rate-Limiting & Brute-Force-Schutz (IP-basiert, in-memory)
- Management Audit-Log (Migration 007, vollständige Historie)
- Widerruf-Verhalten korrekt implementiert
- Alle Backend-Tests erfolgreich
Frontend (Tasks 12-18):
- Management-Portal UI (/manage/:token) ⏳
- Consent-Management UI ⏳
- Metadata-Edit UI ⏳
- Bilder-Management UI ⏳
- Gruppe löschen UI ⏳
- Upload-Erfolgsseite mit Management-Link ⏳
- E2E Testing ⏳
Deployment (Tasks 19-20):
- Dokumentation aktualisiert ⏳
- nginx Konfiguration ⏳
- Production-Deployment ⏳
📅 Zeitplan
Phase 1 (Must-Have): ✅ 100% KOMPLETT in 2 Tagen (9-10. Nov 2025)
- Tag 1 (9. Nov): Backend komplett (Migrationen, Repositories, API-Routes, Upload-Validation)
- Tag 1 (9. Nov): Frontend komplett (ConsentCheckboxes, Upload-Integration, Moderation-Features)
- Tag 2 (10. Nov): Bug-Fixes (Filter-Logik, groupFormatter, display_in_workshop)
- Tag 2 (10. Nov): GDPR-Compliance Fix (Migration 005 korrigiert & validiert)
- Tag 2 (10. Nov): DatabaseManager-Fix (inline Kommentare in Migrationen)
- Tag 2 (10. Nov): Validierung mit 72 Produktionsgruppen (alle GDPR-konform)
Tatsächliche Implementierungszeit: Deutlich schneller als geplant (2 statt 4-5 Tage)
Finale Commits: 12 Commits, Branch: feature/SocialMedia
Status: Production-ready nach Code-Review
Phase 2 (Nice-to-Have): ✅ Backend 100% komplett (11. Nov 2025) | ⏳ Frontend ausstehend
Backend (Tasks 2-11) - ✅ KOMPLETT:
- ✅ Task 2: Token-Generation (UUID v4 bei Upload, bereits in Phase 1)
- ✅ Task 3: Token-Validierung API (GET /api/manage/:token)
- ✅ Task 4: Consent-Widerruf API (PUT /api/manage/:token/consents)
- ✅ Task 5: Metadata-Edit API (PUT /api/manage/:token/metadata)
- ✅ Task 6: Bilder hinzufügen API (POST /api/manage/:token/images)
- ✅ Task 7: Bild löschen API (DELETE /api/manage/:token/images/:imageId)
- ✅ Task 8: Gruppe löschen API (DELETE /api/manage/:token)
- ✅ Task 9: Rate-Limiting & Brute-Force-Schutz (10 req/h, 20 Versuche → 24h Block)
- ✅ Task 10: Management Audit-Log (Migration 007, Repository, Admin-Endpoints)
- ✅ Task 11: Widerruf-Verhalten validiert (Workshop: display_in_workshop=0, Social Media: revoked=1)
Frontend (Tasks 12-18) - ⏳ IN ARBEIT (13. Nov 2025):
- ✅ Task 12: Management Portal Grundgerüst (/manage/:token Route) - KOMPLETT
- ⏳ Task 13: Consent-Management UI (Widerruf/Wiederherstellen) - KOMPLETT (in Task 12 integriert)
- ⏳ Task 14: Metadata-Edit UI (Titel/Beschreibung ändern) - KOMPLETT (in Task 12 integriert)
- ⏳ Task 15: Bilder-Management UI (Hinzufügen/Löschen) - KOMPLETT (in Task 12 integriert)
- ⏳ Task 16: Gruppe löschen UI (mit Bestätigung) - KOMPLETT (in Task 12 integriert)
- ⏳ Task 17: Upload-Erfolgsseite (Management-Link prominent anzeigen)
- ⏳ Task 18: E2E Testing (alle Flows testen)
Dokumentation & Deployment (Tasks 19-20) - ⏳ IN ARBEIT (13. Nov 2025):
- ⏳ Task 19: Dokumentation aktualisieren
- ✅ Task 20: nginx Konfiguration (/api/manage/* Routing) - KOMPLETT
Zeitaufwand Phase 2:
- Backend: 1 Tag (11. Nov 2025) - ✅ komplett
- Frontend Tasks 12 & 20: 1 Tag (13. Nov 2025) - ✅ komplett
- Testing & Deployment: Geplant ~1 Tag
🐛 Bekannte Issues & Fixes
Issue 1: Filter zeigte keine Bilder (9. Nov) - ✅ GELÖST
Problem: getGroupsByConsentStatus() gab nur Metadaten ohne Bilder zurück
Lösung: Filter lädt ALLE Gruppen mit getAllGroupsWithModerationInfo(), dann In-Memory-Filterung
Issue 2: "Nur Werkstatt" Filter zeigte nichts (9. Nov) - ✅ GELÖST
Problem: Filter prüfte array.length === 0 statt consent.consented === 1
Lösung: Korrekte Boolean-Prüfung auf consented Feld
Issue 3: Alle Filter gaben 0 Gruppen zurück (9. Nov) - ✅ GELÖST
Problem: display_in_workshop fehlte in groupFormatter.formatGroupDetail()
Lösung: Feld hinzugefügt in Commit f049c47
Issue 4: GDPR-Verletzung in Migration 005 (10. Nov) - ✅ GELÖST & VALIDIERT
Problem: UPDATE groups SET display_in_workshop = 1 setzte alle alten Gruppen auf "consented"
Lösung: UPDATE entfernt, alte Gruppen bleiben bei display_in_workshop = 0 (expliziter Consent erforderlich)
Test: Mit 72 Produktionsgruppen validiert - alle haben display_in_workshop = 0 ✅
Issue 5: Automatisches Migrationssystem - inline Kommentare (10. Nov) - ✅ GELÖST
Problem: SQL-Statements mit inline Kommentaren (z.B. TEXT; -- comment) wurden fehlerhaft geparst
Lösung: DatabaseManager entfernt jetzt alle Kommentare (Zeilen- und inline) vor dem Statement-Split
Commit: 8e62475 - "fix: DatabaseManager removes inline comments correctly in migrations"
Test: Migration 005 & 006 laufen jetzt automatisch beim Backend-Start ✅
Issue 6: ModerationGroupsPage - Filter "Alle Gruppen" (13. Nov) - ⚠️ OFFEN
Problem: Filter "Alle Gruppen" auf ModerationGroupsPage.js funktioniert nicht (mehr?)
Status: Neu entdeckt während Testing von Tasks 12 & 20
Next: Separate Bugfix-Session nach Commit von Tasks 12 & 20
Issue 7: Export-Button funktioniert nicht (13. Nov) - ⚠️ OFFEN
Problem: "Consent-Daten exportieren" Button funktioniert nicht (mehr?)
Status: Neu entdeckt während Testing von Tasks 12 & 20
Next: Separate Bugfix-Session nach Commit von Tasks 12 & 20
📊 Implementierungsergebnis
Phase 1 (9-10. Nov 2025)
Git-Historie (Branch: feature/SocialMedia):
- 11 Commits vom 9-10. November 2025
- Letzter Commit:
8e62475- "fix: DatabaseManager removes inline comments correctly in migrations" - Status: 100% komplett - Production-ready
API-Endpoints:
- ✅
GET /api/social-media/platforms- Liste aktiver Social Media Plattformen - ✅
POST /api/groups/:groupId/consents- Consents speichern - ✅
GET /api/groups/:groupId/consents- Consents abrufen - ✅
GET /api/admin/groups/by-consent- Gruppen nach Consent filtern - ✅
GET /api/admin/consents/export- Consent-Daten exportieren (CSV/JSON)
Test-Ergebnisse (10. Nov 2025):
- ✅ Upload mit Consent: Funktioniert
- ✅ Upload ohne Werkstatt-Consent: Blockiert (400 Error)
- ✅ Filter "Alle Gruppen": 76 Gruppen
- ✅ Filter "Nur Werkstatt": 74 Gruppen
- ✅ Filter "Facebook": 2 Gruppen
- ✅ Export-Button: CSV-Download funktioniert
- ✅ ConsentBadges: Icons und Tooltips werden korrekt angezeigt
- ✅ Automatische Migration: Migration 005 & 006 beim Backend-Start angewendet
- ✅ GDPR-Konformität: 72 alte Gruppen mit display_in_workshop = 0
- ✅ Social Media Plattformen: 3 Plattformen (Facebook, Instagram, TikTok)
Phase 2 Backend (11. Nov 2025)
Git-Historie:
- 4 neue Commits am 11. November 2025
c18c258- "feat(phase2): Implement Management Portal API routes (Tasks 3-7)"2d49f0b- "fix(phase2): Fix DELETE /api/manage/:token - use correct DeletionLogRepository method"0dce5fd- "feat(phase2): Implement Rate-Limiting & Brute-Force Protection (Task 9)"0f77db6- "feat(phase2): Implement Management Audit-Log (Task 10)"
- Gesamtstand: 15 Commits (11 Phase 1 + 4 Phase 2)
- Status: Backend 100% komplett - Bereit für Frontend-Integration
Neue Dateien erstellt:
backend/src/routes/management.js(651 Zeilen) - 8 Management-API-Routesbackend/src/middlewares/rateLimiter.js(~180 Zeilen) - Rate-Limiting & Brute-Force-Schutzbackend/src/middlewares/auditLog.js(~45 Zeilen) - Audit-Logging-Middlewarebackend/src/repositories/ManagementAuditLogRepository.js(~180 Zeilen) - Audit-Log CRUDbackend/src/database/migrations/007_create_management_audit_log.sql- Audit-Log-Tabelle
Erweiterte Dateien:
backend/src/repositories/GroupRepository.js-getGroupByManagementToken()Methodebackend/src/routes/admin.js- 3 neue Audit-Log-Endpointsbackend/src/routes/index.js- Management-Router registriertbackend/package.json-uuidDependency hinzugefügt
Phase 2 Frontend (13. Nov 2025)
Git-Historie:
- 1 Commit geplant für Tasks 12 & 20
- Gesamtstand nach Commit: 16 Commits (11 Phase 1 + 4 Phase 2 Backend + 1 Phase 2 Frontend)
- Status: Tasks 12 & 20 komplett - Bereit für Commit & Merge
Neue Dateien erstellt:
frontend/src/Components/Pages/ManagementPortalPage.js(~650 Zeilen) - Self-Service-Portal
Erweiterte Dateien:
frontend/src/App.js- Route/manage/:tokenhinzugefügtfrontend/src/Components/ComponentUtils/Headers/Navbar.js- Conditional "Mein Upload" Buttondocker/dev/frontend/nginx.conf- Proxy/api/manage/*zu backend-devdocker/prod/frontend/nginx.conf- Proxy/api/manage/*zu backend
Task 12 - ManagementPortalPage Implementierung:
-
✅ Komponentenwiederverwertung (User-Anforderung: "Bitte das Rad nicht neu erfinden"):
ImageGalleryCard- Gruppen-ÜbersichtImageGallery- Bildergalerie mit Lösch-FunktionalitätDescriptionInput- Metadata-Formular (Titel, Beschreibung, Jahr)ConsentBadges- Consent-Status-Anzeige (Workshop & Social Media)Navbar&Footer- Layout-Komponenten
-
✅ Layout & UX:
- Single-Page-Design ohne Tabs (konsistent mit ModerationGroupImagesPage)
- Scrollbare Sections: Overview → Consent Management → Images → Metadata → Delete Group
- Responsive Material-UI Layout (Paper, Container, Box, Typography)
- SweetAlert2 Confirmations für destructive Actions
-
✅ CRUD-Operationen:
loadGroup()- GET /api/manage/:token, Data-Transformation (camelCase → snake_case)handleSaveMetadata()- PUT /api/manage/:token/metadata (mit Approval-Reset-Warning)handleRemoveImage()- DELETE /api/manage/:token/images/:imageId (SweetAlert-Confirmation)handleRevokeConsent()- PUT /api/manage/:token/consents (Workshop & Social Media separat)handleRestoreConsent()- PUT /api/manage/:token/consents (Wiederherstellen)handleDeleteGroup()- DELETE /api/manage/:token (Double-Confirmation: Checkbox + Button)handleEditMode()- Toggle Edit-Mode für BildbeschreibungenhandleDescriptionChange()- Bildbeschreibungen ändern (max 200 Zeichen)
-
✅ Fehlerbehandlung:
- 404: Ungültiger Token → "Zugriff nicht möglich. Ungültiger oder abgelaufener Link"
- 429: Rate-Limit → "Zu viele Anfragen. Bitte versuchen Sie es später erneut"
- Allgemeine Fehler → "Fehler beim Laden der Gruppe"
- Netzwerkfehler → User-freundliche Meldungen
-
✅ Data-Transformation:
- Backend liefert camelCase (displayInWorkshop, consentTimestamp)
- ConsentBadges erwartet snake_case (display_in_workshop, consent_timestamp)
- loadGroup() transformiert Daten für Kompatibilität (beide Formate verfügbar)
Task 20 - nginx Konfiguration:
-
✅ Dev-Environment (
docker/dev/frontend/nginx.conf):location /api/manage { proxy_pass http://backend-dev:5000/api/manage; 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; } -
✅ Prod-Environment (
docker/prod/frontend/nginx.conf):location /api/manage { proxy_pass http://image-uploader-backend:5000/api/manage; 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; } -
✅ Container Rebuild: Frontend-Container neu gebaut mit
docker compose up -d --build frontend-dev
Navigation Enhancement (Navbar.js):
- ✅ Conditional Rendering mit
useLocation()Hook - ✅ "Upload" Button immer sichtbar (nur aktiv auf
/) - ✅ "Mein Upload" Button zusätzlich auf
/manage/:token(aktiv) - ✅ Beide Buttons gleichzeitig auf Management-Seite (User-Anforderung)
Test-Ergebnisse (13. Nov 2025):
- ✅ Token-Validierung: GET /api/manage/:token funktioniert (200 mit Daten, 404 bei ungültig)
- ✅ API-Routing: nginx routet /api/manage/* korrekt zu Backend
- ✅ ConsentBadges: Workshop & Social Media Icons korrekt angezeigt
- ✅ Consent-Widerruf: Workshop & Social Media Widerruf funktioniert
- ✅ Consent-Wiederherstellen: Funktioniert korrekt
- ✅ Metadata-Edit: Titel & Beschreibung ändern, setzt approved=0
- ✅ Bild-Löschen: Funktioniert mit Bestätigung, verhindert letztes Bild löschen
- ✅ Gruppe-Löschen: Double-Confirmation (Checkbox + Button)
- ✅ Rate-Limiting: 429-Error bei >10 Requests/Stunde (Backend-Restart behebt in Dev)
- ✅ Navigation: "Upload" & "Mein Upload" Buttons korrekt sichtbar/aktiv
- ✅ Data-Transformation: camelCase ↔ snake_case funktioniert
- ✅ Component-Reuse: 0 Zeilen duplizierter Code
- ✅ Browser-Testing: Alle Funktionen in Chrome getestet
Bekannte Issues nach Testing:
- ⚠️ Issue 6: ModerationGroupsPage - Filter "Alle Gruppen" funktioniert nicht
- ⚠️ Issue 7: Export-Button "Consent-Daten exportieren" funktioniert nicht
Status: ✅ Tasks 12 & 20 komplett | Bereit für Commit & Merge
Management Portal APIs (alle getestet):
- ✅
GET /api/manage/:token- Token validieren & Gruppendaten laden - ✅
PUT /api/manage/:token/consents- Consents widerrufen/wiederherstellen - ✅
PUT /api/manage/:token/metadata- Titel & Beschreibung editieren (setzt approved=0) - ✅
POST /api/manage/:token/images- Bilder hinzufügen (max 50, setzt approved=0) - ✅
DELETE /api/manage/:token/images/:imageId- Einzelnes Bild löschen - ✅
DELETE /api/manage/:token- Komplette Gruppe löschen
Management Audit-Log APIs (alle getestet):
- ✅
GET /api/admin/management-audit?limit=N- Letzte N Audit-Log-Einträge - ✅
GET /api/admin/management-audit/stats- Statistiken (Aktionen, IPs, Erfolgsrate) - ✅
GET /api/admin/management-audit/group/:groupId- Audit-Log für spezifische Gruppe
Sicherheitsfeatures:
- ✅ Rate-Limiting: IP-basiert, 10 Anfragen/Stunde
- ✅ Brute-Force-Schutz: 20 fehlgeschlagene Versuche → 24h IP-Block
- ✅ Audit-Logging: Alle Management-Aktionen werden protokolliert
- ✅ Token-Maskierung: Nur erste 8 Zeichen im Audit-Log gespeichert
- ✅ File-Cleanup: Gelöschte Bilder werden physisch von Festplatte entfernt
- ✅ Validation: UUID-Format-Check, Image-Count-Limits, Duplicate-Prevention
Test-Ergebnisse (11. Nov 2025):
- ✅ Token-Validierung: GET /api/manage/:token funktioniert (200 mit Daten, 404 bei invalid)
- ✅ Consent-Widerruf: Workshop setzt display_in_workshop=0, Social Media setzt revoked=1
- ✅ Metadata-Edit: Titel/Beschreibung ändern, setzt approved=0
- ✅ Bilder hinzufügen: POST /api/manage/:token/images (max 50 Bilder-Limit)
- ✅ Bild löschen: DELETE .../:imageId funktioniert, verhindert letztes Bild löschen
- ✅ Gruppe löschen: DELETE /api/manage/:token mit Deletion-Log
- ✅ Rate-Limiting: Blockiert nach 10 Requests/Stunde (429 Error)
- ✅ Audit-Log: 2 Einträge geschrieben, Admin-API funktioniert
- ✅ Migration 007: Erfolgreich angewendet nach DB-Reset
- ✅ GDPR-Konformität: 72 alte Gruppen mit display_in_workshop = 0, 0 mit automatischem Consent
- ✅ Social Media Plattformen: 3 Plattformen (Facebook, Instagram, TikTok) erfolgreich angelegt
<EFBFBD>🔗 Abhängigkeiten
Externe Libraries
- Keine neuen Dependencies für Phase 1 (nutzt vorhandene Material-UI)
- Phase 2: Optional
nodemailerfür E-Mail-Benachrichtigungen
Interne Abhängigkeiten
- Basiert auf bestehendem Upload-Flow
- Nutzt vorhandene Datenbank-Infrastruktur
- Integration in bestehendes Moderation-Panel
📚 Referenzen
- DSGVO Art. 7 - Bedingungen für die Einwilligung
- Material-UI Checkbox Documentation
- SQLite Foreign Key Support
- UUID v4 Best Practices
Erstellt am: 9. November 2025
Letzte Aktualisierung: 11. November 2025, 20:30 Uhr
Status: ✅ Phase 1: 100% komplett | ✅ Phase 2 Backend: 100% komplett | ⏳ Phase 2 Frontend: ausstehend
Production-Ready: Phase 1: Ja (deployed) | Phase 2 Backend: Ja (bereit für Frontend-Integration)