feat(backend): add GET/PUT /api/consents endpoints and requireConsent middleware

This commit is contained in:
Antoni Nuñez Romeu
2026-08-26 15:05:00 +02:00
parent bc689dad68
commit aea8319ac7
+87
View File
@@ -955,6 +955,93 @@ const requireAdmin = (req, res, next) => {
return res.status(401).json({ error: 'Authentication required' }); return res.status(401).json({ error: 'Authentication required' });
}; };
// Middleware to check if user has granted a specific consent category
const requireConsent = (category) => {
return async (req, res, next) => {
try {
const userId = req.session?.userId;
const sessionId = req.sessionID;
let consent;
if (userId) {
consent = await userDbGet('SELECT granted FROM user_consents WHERE user_id = ? AND category = ?', [userId, category]);
} else if (sessionId) {
consent = await userDbGet('SELECT granted FROM user_consents WHERE session_id = ? AND category = ?', [sessionId, category]);
}
if (consent && consent.granted) return next();
return res.status(403).json({ error: `Consent required: ${category}` });
} catch (error) {
console.error('Consent check error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
};
// ========== CONSENT MANAGEMENT ==========
app.get('/api/consents', async (req, res) => {
try {
const userId = req.session?.userId;
const sessionId = req.sessionID;
const categories = ['essential', 'analytics', 'preferences', 'health_data'];
const result = {};
categories.forEach(c => { result[c] = c === 'essential'; });
if (userId) {
const rows = await userDbAll('SELECT category, granted FROM user_consents WHERE user_id = ?', [userId]);
rows.forEach(r => { result[r.category] = r.granted; });
} else if (sessionId) {
const rows = await userDbAll('SELECT category, granted FROM user_consents WHERE session_id = ?', [sessionId]);
rows.forEach(r => { result[r.category] = r.granted; });
}
result.essential = true;
res.json(result);
} catch (error) {
console.error('Error fetching consents:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.put('/api/consents', async (req, res) => {
try {
const userId = req.session?.userId;
const sessionId = req.sessionID;
const { categories } = req.body;
if (!categories || typeof categories !== 'object') {
return res.status(400).json({ error: 'categories object required' });
}
const allowedCategories = ['analytics', 'preferences', 'health_data'];
for (const cat of allowedCategories) {
if (cat in categories) {
const granted = Boolean(categories[cat]);
if (userId) {
await userDbRun(
`INSERT INTO user_consents (user_id, category, granted, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, category) DO UPDATE SET granted = ?, updated_at = CURRENT_TIMESTAMP`,
[userId, cat, granted, granted]
);
} else if (sessionId) {
await userDbRun(
`INSERT INTO user_consents (session_id, category, granted, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (session_id, category) DO UPDATE SET granted = ?, updated_at = CURRENT_TIMESTAMP`,
[sessionId, cat, granted, granted]
);
}
}
}
// Return updated consents
const result = { essential: true };
const cats = ['analytics', 'preferences', 'health_data'];
if (userId) {
const rows = await userDbAll('SELECT category, granted FROM user_consents WHERE user_id = ?', [userId]);
rows.forEach(r => { result[r.category] = r.granted; });
} else if (sessionId) {
const rows = await userDbAll('SELECT category, granted FROM user_consents WHERE session_id = ?', [sessionId]);
rows.forEach(r => { result[r.category] = r.granted; });
}
cats.forEach(c => { if (!(c in result)) result[c] = false; });
res.json(result);
} catch (error) {
console.error('Error saving consents:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// ========== RECENT SEARCHES (database-based) ========== // ========== RECENT SEARCHES (database-based) ==========
const MAX_RECENT = 5; const MAX_RECENT = 5;