Project-Image-Uploader/docs/FEATURE_PLAN-social-media.md
matthias.lotz e8ba1e73a0 feat(phase2): Implement Frontend Management Portal & nginx routing (Tasks 12, 20)
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)
2025-11-13 20:05:27 +01:00

55 KiB
Raw Blame History

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

Backend Tasks

Task 1.1: Datenbank-Migrationen ⏱️ 1-2h KOMPLETT ERLEDIGT

  • Migration 005: display_in_workshop, consent_timestamp, management_token zu groups hinzufügen
  • Migration 006: social_media_platforms und group_social_media_consents Tabellen 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() implementieren
  • GroupRepository: getGroupWithConsents() implementieren
  • GroupRepository: getGroupsByConsentStatus() für Moderation-Filter
  • SocialMediaRepository: Neue Klasse erstellen
  • SocialMediaRepository: Platform-Management-Methoden
  • SocialMediaRepository: 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/platforms erstellen
  • Route POST /api/groups/:groupId/consents erstellen
  • Route GET /api/groups/:groupId/consents erstellen
  • Route GET /api/admin/groups/by-consent für Moderation-Filter
  • Route GET /api/admin/consents/export fü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: workshopConsent muss true sein
  • Consent-Daten mit Gruppe speichern
  • Timestamp setzen
  • Response um groupId erweitern
  • 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_token in 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

  • revoked und revoked_timestamp in 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/:token erstellen
  • 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

  1. Upload mit Pflicht-Zustimmung

    • Upload ohne Werkstatt-Zustimmung wird blockiert
    • Upload mit Werkstatt-Zustimmung funktioniert
    • Consent-Timestamp wird korrekt gespeichert
  2. 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
  3. Upload-Success Dialog

    • Gruppen-ID wird angezeigt
    • Copy-to-Clipboard funktioniert
    • Informationstexte sind korrekt
  4. 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

  1. 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
  2. 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-Routes
  • backend/src/middlewares/rateLimiter.js (~180 Zeilen) - Rate-Limiting & Brute-Force-Schutz
  • backend/src/middlewares/auditLog.js (~45 Zeilen) - Audit-Logging-Middleware
  • backend/src/repositories/ManagementAuditLogRepository.js (~180 Zeilen) - Audit-Log CRUD
  • backend/src/database/migrations/007_create_management_audit_log.sql - Audit-Log-Tabelle

Erweiterte Dateien:

  • backend/src/repositories/GroupRepository.js - getGroupByManagementToken() Methode
  • backend/src/routes/admin.js - 3 neue Audit-Log-Endpoints
  • backend/src/routes/index.js - Management-Router registriert
  • backend/package.json - uuid Dependency 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/:token hinzugefügt
  • frontend/src/Components/ComponentUtils/Headers/Navbar.js - Conditional "Mein Upload" Button
  • docker/dev/frontend/nginx.conf - Proxy /api/manage/* zu backend-dev
  • docker/prod/frontend/nginx.conf - Proxy /api/manage/* zu backend

Task 12 - ManagementPortalPage Implementierung:

  • Komponentenwiederverwertung (User-Anforderung: "Bitte das Rad nicht neu erfinden"):

    • ImageGalleryCard - Gruppen-Übersicht
    • ImageGallery - Bildergalerie mit Lösch-Funktionalität
    • DescriptionInput - 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 Bildbeschreibungen
    • handleDescriptionChange() - 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 nodemailer für E-Mail-Benachrichtigungen

Interne Abhängigkeiten

  • Basiert auf bestehendem Upload-Flow
  • Nutzt vorhandene Datenbank-Infrastruktur
  • Integration in bestehendes Moderation-Panel

📚 Referenzen


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)