Project-Image-Uploader/backend/src/routes/consent.js
matthias.lotz 76aa028686 fix: Add /api prefix to consent routes and nginx proxy config
- Update consent.js routes to use /api prefix
- Add /api/social-media location to dev/prod nginx configs
- Fix route registration for proper API access
2025-11-09 21:22:35 +01:00

305 lines
9.2 KiB
JavaScript

/**
* Consent Management API Routes
*
* Handles social media platform listings and consent management
*/
const express = require('express');
const router = express.Router();
const GroupRepository = require('../repositories/GroupRepository');
const SocialMediaRepository = require('../repositories/SocialMediaRepository');
const dbManager = require('../database/DatabaseManager');
// ============================================================================
// Social Media Platforms
// ============================================================================
/**
* GET /api/social-media/platforms
* Liste aller aktiven Social Media Plattformen
*/
router.get('/api/social-media/platforms', async (req, res) => {
try {
const socialMediaRepo = new SocialMediaRepository(dbManager);
const platforms = await socialMediaRepo.getActivePlatforms();
res.json(platforms);
} catch (error) {
console.error('Error fetching platforms:', error);
res.status(500).json({
error: 'Failed to fetch social media platforms',
message: error.message
});
}
});
// ============================================================================
// Group Consents
// ============================================================================
/**
* POST /api/groups/:groupId/consents
* Speichere oder aktualisiere Consents für eine Gruppe
*
* Body: {
* workshopConsent: boolean,
* socialMediaConsents: [{ platformId: number, consented: boolean }]
* }
*/
router.post('/api/groups/:groupId/consents', async (req, res) => {
try {
const { groupId } = req.params;
const { workshopConsent, socialMediaConsents } = req.body;
// Validierung
if (typeof workshopConsent !== 'boolean') {
return res.status(400).json({
error: 'Invalid request',
message: 'workshopConsent must be a boolean'
});
}
if (!Array.isArray(socialMediaConsents)) {
return res.status(400).json({
error: 'Invalid request',
message: 'socialMediaConsents must be an array'
});
}
// Prüfe ob Gruppe existiert
const group = await GroupRepository.getGroupById(groupId);
if (!group) {
return res.status(404).json({
error: 'Group not found',
message: `No group found with ID: ${groupId}`
});
}
// Aktualisiere Consents
await GroupRepository.updateConsents(
groupId,
workshopConsent,
socialMediaConsents
);
res.json({
success: true,
message: 'Consents updated successfully',
groupId
});
} catch (error) {
console.error('Error updating consents:', error);
res.status(500).json({
error: 'Failed to update consents',
message: error.message
});
}
});
/**
* GET /api/groups/:groupId/consents
* Lade alle Consents für eine Gruppe
*/
router.get('/api/groups/:groupId/consents', async (req, res) => {
try {
const { groupId } = req.params;
// Hole Gruppe mit Consents
const group = await GroupRepository.getGroupWithConsents(groupId);
if (!group) {
return res.status(404).json({
error: 'Group not found',
message: `No group found with ID: ${groupId}`
});
}
// Formatiere Response
const response = {
groupId: group.group_id,
workshopConsent: group.display_in_workshop === 1,
consentTimestamp: group.consent_timestamp,
socialMediaConsents: group.consents.map(c => ({
platformId: c.platform_id,
platformName: c.platform_name,
displayName: c.display_name,
iconName: c.icon_name,
consented: c.consented === 1,
consentTimestamp: c.consent_timestamp,
revoked: c.revoked === 1,
revokedTimestamp: c.revoked_timestamp
}))
};
res.json(response);
} catch (error) {
console.error('Error fetching consents:', error);
res.status(500).json({
error: 'Failed to fetch consents',
message: error.message
});
}
});
// ============================================================================
// Admin - Filtering & Export
// ============================================================================
/**
* GET /api/admin/groups/by-consent
* Filtere Gruppen nach Consent-Status
*
* Query params:
* - displayInWorkshop: boolean
* - platformId: number
* - platformConsent: boolean
*/
router.get('/admin/groups/by-consent', async (req, res) => {
try {
const filters = {};
// Parse query parameters
if (req.query.displayInWorkshop !== undefined) {
filters.displayInWorkshop = req.query.displayInWorkshop === 'true';
}
if (req.query.platformId !== undefined) {
filters.platformId = parseInt(req.query.platformId, 10);
if (isNaN(filters.platformId)) {
return res.status(400).json({
error: 'Invalid platformId',
message: 'platformId must be a number'
});
}
}
if (req.query.platformConsent !== undefined) {
filters.platformConsent = req.query.platformConsent === 'true';
}
// Hole gefilterte Gruppen
const groups = await GroupRepository.getGroupsByConsentStatus(filters);
res.json({
count: groups.length,
filters,
groups
});
} catch (error) {
console.error('Error filtering groups by consent:', error);
res.status(500).json({
error: 'Failed to filter groups',
message: error.message
});
}
});
/**
* GET /api/admin/consents/export
* Export Consent-Daten für rechtliche Dokumentation
*
* Query params:
* - format: 'json' | 'csv' (default: json)
* - year: number (optional filter)
* - approved: boolean (optional filter)
*/
router.get('/admin/consents/export', async (req, res) => {
try {
const format = req.query.format || 'json';
const filters = {};
// Parse filters
if (req.query.year) {
filters.year = parseInt(req.query.year, 10);
}
if (req.query.approved !== undefined) {
filters.approved = req.query.approved === 'true';
}
// Export Daten holen
const exportData = await GroupRepository.exportConsentData(filters);
// Format: JSON
if (format === 'json') {
res.json({
exportDate: new Date().toISOString(),
filters,
count: exportData.length,
data: exportData
});
return;
}
// Format: CSV
if (format === 'csv') {
// CSV Header
let csv = 'group_id,year,title,name,upload_date,workshop_consent,consent_timestamp,approved';
// Sammle alle möglichen Plattformen
const allPlatforms = new Set();
exportData.forEach(group => {
group.socialMediaConsents.forEach(consent => {
allPlatforms.add(consent.platform_name);
});
});
// Füge Platform-Spalten hinzu
const platformNames = Array.from(allPlatforms).sort();
platformNames.forEach(platform => {
csv += `,${platform}`;
});
csv += '\n';
// CSV Daten
exportData.forEach(group => {
const row = [
group.group_id,
group.year,
`"${(group.title || '').replace(/"/g, '""')}"`,
`"${(group.name || '').replace(/"/g, '""')}"`,
group.upload_date,
group.display_in_workshop === 1 ? 'true' : 'false',
group.consent_timestamp || '',
group.approved === 1 ? 'true' : 'false'
];
// Platform-Consents
const consentMap = {};
group.socialMediaConsents.forEach(consent => {
consentMap[consent.platform_name] = consent.consented === 1;
});
platformNames.forEach(platform => {
row.push(consentMap[platform] ? 'true' : 'false');
});
csv += row.join(',') + '\n';
});
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename=consent-export-${Date.now()}.csv`);
res.send(csv);
return;
}
res.status(400).json({
error: 'Invalid format',
message: 'Format must be "json" or "csv"'
});
} catch (error) {
console.error('Error exporting consent data:', error);
res.status(500).json({
error: 'Failed to export consent data',
message: error.message
});
}
});
module.exports = router;