- Create consent.js with comprehensive API endpoints: - GET /api/social-media/platforms - list active platforms - POST /api/groups/:groupId/consents - save/update group consents - GET /api/groups/:groupId/consents - retrieve group consent data - GET /api/admin/groups/by-consent - filter groups by consent status - GET /api/admin/consents/export - export consent data (JSON/CSV formats) - Register consent router in routes/index.js - Full validation and error handling - CSV export with dynamic platform columns - Ready for frontend integration
305 lines
9.2 KiB
JavaScript
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('/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('/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('/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;
|