Files
opencrm/backend/src/controllers/consent-public.controller.ts
T

170 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Request, Response } from 'express';
import * as consentPublicService from '../services/consent-public.service.js';
import { createAuditLog } from '../services/audit.service.js';
import { CONSENT_TYPE_LABELS } from '../services/consent.service.js';
import { ConsentType } from '@prisma/client';
import { sendEmail } from '../services/smtpService.js';
import { getSystemEmailCredentials } from '../services/emailProvider/emailProviderService.js';
/**
* Öffentliche Consent-Seite: Kundendaten + Datenschutztext + Status
*/
export async function getConsentPage(req: Request, res: Response) {
try {
const { hash } = req.params;
const result = await consentPublicService.getCustomerByConsentHash(hash);
if (!result) {
return res.status(404).json({ success: false, error: 'Ungültiger Link' });
}
const privacyPolicyHtml = await consentPublicService.getPrivacyPolicyHtml(result.customer.id);
// Consent-Status mit Labels
const consentsWithLabels = result.consents.map((c) => ({
consentType: c.consentType,
status: c.status,
label: CONSENT_TYPE_LABELS[c.consentType as ConsentType]?.label || c.consentType,
description: CONSENT_TYPE_LABELS[c.consentType as ConsentType]?.description || '',
grantedAt: c.grantedAt,
}));
res.json({
success: true,
data: {
customer: {
firstName: result.customer.firstName,
lastName: result.customer.lastName,
customerNumber: result.customer.customerNumber,
},
privacyPolicyHtml,
consents: consentsWithLabels,
allGranted: consentsWithLabels.every((c) => c.status === 'GRANTED'),
},
});
} catch (error) {
console.error('Fehler beim Laden der Consent-Seite:', error);
res.status(500).json({ success: false, error: 'Fehler beim Laden' });
}
}
/**
* Alle 4 Einwilligungen erteilen (öffentlicher Link)
*/
export async function grantAllConsents(req: Request, res: Response) {
try {
const { hash } = req.params;
const ipAddress = req.ip || req.socket.remoteAddress || 'unknown';
const results = await consentPublicService.grantAllConsentsPublic(hash, ipAddress);
// Audit-Log (manuell, da keine Auth-Middleware)
const customer = await consentPublicService.getCustomerByConsentHash(hash);
if (customer) {
for (const type of Object.values(ConsentType)) {
await createAuditLog({
userEmail: customer.customer.email || 'public-link',
action: 'UPDATE',
sensitivity: 'HIGH',
resourceType: 'CustomerConsent',
resourceId: `${customer.customer.id}:${type}`,
resourceLabel: `Einwilligung ${type} erteilt via Public-Link`,
endpoint: `/api/public/consent/${hash}/grant`,
httpMethod: 'POST',
ipAddress,
dataSubjectId: customer.customer.id,
legalBasis: 'DSGVO Art. 6 Abs. 1 lit. a',
});
}
}
// Bestätigungs-E-Mail senden
if (customer?.customer.email) {
try {
const systemEmail = await getSystemEmailCredentials();
if (systemEmail) {
const consentList = Object.values(ConsentType)
.map(t => CONSENT_TYPE_LABELS[t]?.label || t)
.map(label => `<li>${label}</li>`)
.join('');
const confirmationHtml = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #16a34a;">Bestätigung Ihrer Einwilligungen</h2>
<p>Sehr geehrte(r) ${customer.customer.firstName} ${customer.customer.lastName},</p>
<p>
vielen Dank! Hiermit bestätigen wir, dass Sie am ${new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })}
folgenden Einwilligungen zugestimmt haben:
</p>
<ul style="color: #16a34a;">
${consentList}
</ul>
<p>
Sie können Ihre Einwilligungen jederzeit über Ihr Kundenportal widerrufen.
</p>
<hr style="border: none; border-top: 1px solid #e5e7eb; margin: 24px 0;">
<p style="color: #9ca3af; font-size: 12px;">
Hacker-Net Telekommunikation Stefan Hacker<br>
Am Wunderburgpark 5b, 26135 Oldenburg<br>
info@hacker-net.de
</p>
</div>
`;
await sendEmail(
{
host: systemEmail.smtpServer,
port: systemEmail.smtpPort,
user: systemEmail.emailAddress,
password: systemEmail.password,
encryption: systemEmail.smtpEncryption,
allowSelfSignedCerts: systemEmail.allowSelfSignedCerts,
},
systemEmail.emailAddress,
{
to: customer.customer.email,
subject: 'Bestätigung Ihrer Datenschutz-Einwilligungen',
html: confirmationHtml,
},
{
context: 'consent-confirmation',
customerId: customer.customer.id,
}
);
}
} catch (emailError) {
// E-Mail-Fehler soll den Consent-Grant nicht blockieren
console.error('Bestätigungs-E-Mail konnte nicht gesendet werden:', emailError);
}
}
res.json({ success: true, data: results });
} catch (error: any) {
console.error('Fehler beim Erteilen der Einwilligungen:', error);
res.status(400).json({ success: false, error: error.message || 'Fehler beim Erteilen' });
}
}
/**
* Datenschutzerklärung als PDF
*/
export async function getConsentPdf(req: Request, res: Response) {
try {
const { hash } = req.params;
const result = await consentPublicService.getCustomerByConsentHash(hash);
if (!result) {
return res.status(404).json({ success: false, error: 'Ungültiger Link' });
}
const pdfBuffer = await consentPublicService.generateConsentPdf(result.customer.id);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'inline; filename="datenschutzerklaerung.pdf"');
res.send(pdfBuffer);
} catch (error) {
console.error('Fehler beim Generieren des PDFs:', error);
res.status(500).json({ success: false, error: 'Fehler beim Generieren' });
}
}