complete new audit system

This commit is contained in:
2026-03-21 18:23:54 +01:00
parent 38b3b7da73
commit fd55742c57
159 changed files with 2841 additions and 736 deletions
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"developer.routes.d.ts","sourceRoot":"","sources":["../../src/routes/developer.routes.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA0fxB,eAAe,MAAM,CAAC"}
{"version":3,"file":"developer.routes.d.ts","sourceRoot":"","sources":["../../src/routes/developer.routes.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAyfxB,eAAe,MAAM,CAAC"}
+11 -9
View File
@@ -1,22 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const client_1 = require("@prisma/client");
const prisma_js_1 = __importDefault(require("../lib/prisma.js"));
const auth_js_1 = require("../middleware/auth.js");
const router = (0, express_1.Router)();
const prisma = new client_1.PrismaClient();
// Setup-Endpunkt: Erstellt die developer:access Permission und fügt sie der Admin-Rolle hinzu
// Dieser Endpunkt erfordert keine Authentifizierung, da er nur einmalig zum Setup verwendet wird
router.post('/setup', async (req, res) => {
try {
// Create or get the developer:access permission
const developerPerm = await prisma.permission.upsert({
const developerPerm = await prisma_js_1.default.permission.upsert({
where: { resource_action: { resource: 'developer', action: 'access' } },
update: {},
create: { resource: 'developer', action: 'access' },
});
// Get the Admin role
const adminRole = await prisma.role.findUnique({
const adminRole = await prisma_js_1.default.role.findUnique({
where: { name: 'Admin' },
include: { permissions: true },
});
@@ -27,7 +29,7 @@ router.post('/setup', async (req, res) => {
// Check if Admin already has this permission
const hasPermission = adminRole.permissions.some((rp) => rp.permissionId === developerPerm.id);
if (!hasPermission) {
await prisma.rolePermission.create({
await prisma_js_1.default.rolePermission.create({
data: {
roleId: adminRole.id,
permissionId: developerPerm.id,
@@ -286,7 +288,7 @@ router.get('/table/:tableName', auth_js_1.authenticate, (0, auth_js_1.requirePer
}
const skip = (parseInt(page) - 1) * parseInt(limit);
const take = parseInt(limit);
const model = prisma[meta.model];
const model = prisma_js_1.default[meta.model];
const [data, total] = await Promise.all([
model.findMany({
skip,
@@ -339,7 +341,7 @@ router.put('/table/:tableName/:id', auth_js_1.authenticate, (0, auth_js_1.requir
return;
}
}
const model = prisma[meta.model];
const model = prisma_js_1.default[meta.model];
// Composite Primary Key Handling
let where;
if (meta.primaryKey.includes(',')) {
@@ -381,7 +383,7 @@ router.delete('/table/:tableName/:id', auth_js_1.authenticate, (0, auth_js_1.req
res.status(404).json({ success: false, error: 'Tabelle nicht gefunden' });
return;
}
const model = prisma[meta.model];
const model = prisma_js_1.default[meta.model];
// Composite Primary Key Handling
let where;
if (meta.primaryKey.includes(',')) {
@@ -439,7 +441,7 @@ router.get('/reference/:tableName', auth_js_1.authenticate, (0, auth_js_1.requir
res.status(404).json({ success: false, error: 'Tabelle nicht gefunden' });
return;
}
const model = prisma[meta.model];
const model = prisma_js_1.default[meta.model];
const data = await model.findMany({
take: parseInt(limit),
});
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"upload.routes.d.ts","sourceRoot":"","sources":["../../src/routes/upload.routes.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA2vBxB,eAAe,MAAM,CAAC"}
{"version":3,"file":"upload.routes.d.ts","sourceRoot":"","sources":["../../src/routes/upload.routes.ts"],"names":[],"mappings":"AASA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA4wBxB,eAAe,MAAM,CAAC"}
+46 -30
View File
@@ -7,10 +7,10 @@ const express_1 = require("express");
const multer_1 = __importDefault(require("multer"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const client_1 = require("@prisma/client");
const prisma_js_1 = __importDefault(require("../lib/prisma.js"));
const auth_js_1 = require("../middleware/auth.js");
const audit_service_js_1 = require("../services/audit.service.js");
const router = (0, express_1.Router)();
const prisma = new client_1.PrismaClient();
// Uploads-Verzeichnis erstellen falls nicht vorhanden
const uploadsDir = path_1.default.join(process.cwd(), 'uploads');
if (!fs_1.default.existsSync(uploadsDir)) {
@@ -66,7 +66,7 @@ router.post('/bank-cards/:id', auth_js_1.authenticate, (0, auth_js_1.requirePerm
const bankCardId = parseInt(req.params.id);
const relativePath = `/uploads/bank-cards/${req.file.filename}`;
// Bankkarte in der DB aktualisieren
await prisma.bankCard.update({
await prisma_js_1.default.bankCard.update({
where: { id: bankCardId },
data: { documentPath: relativePath },
});
@@ -95,7 +95,7 @@ router.post('/documents/:id', auth_js_1.authenticate, (0, auth_js_1.requirePermi
const documentId = parseInt(req.params.id);
const relativePath = `/uploads/documents/${req.file.filename}`;
// Ausweis in der DB aktualisieren
await prisma.identityDocument.update({
await prisma_js_1.default.identityDocument.update({
where: { id: documentId },
data: { documentPath: relativePath },
});
@@ -119,7 +119,7 @@ router.delete('/bank-cards/:id', auth_js_1.authenticate, (0, auth_js_1.requirePe
try {
const bankCardId = parseInt(req.params.id);
// Bankkarte aus DB holen um Dateipfad zu bekommen
const bankCard = await prisma.bankCard.findUnique({
const bankCard = await prisma_js_1.default.bankCard.findUnique({
where: { id: bankCardId },
});
if (!bankCard) {
@@ -136,7 +136,7 @@ router.delete('/bank-cards/:id', auth_js_1.authenticate, (0, auth_js_1.requirePe
fs_1.default.unlinkSync(filePath);
}
// documentPath in DB auf null setzen
await prisma.bankCard.update({
await prisma_js_1.default.bankCard.update({
where: { id: bankCardId },
data: { documentPath: null },
});
@@ -152,7 +152,7 @@ router.delete('/documents/:id', auth_js_1.authenticate, (0, auth_js_1.requirePer
try {
const documentId = parseInt(req.params.id);
// Ausweis aus DB holen um Dateipfad zu bekommen
const document = await prisma.identityDocument.findUnique({
const document = await prisma_js_1.default.identityDocument.findUnique({
where: { id: documentId },
});
if (!document) {
@@ -169,7 +169,7 @@ router.delete('/documents/:id', auth_js_1.authenticate, (0, auth_js_1.requirePer
fs_1.default.unlinkSync(filePath);
}
// documentPath in DB auf null setzen
await prisma.identityDocument.update({
await prisma_js_1.default.identityDocument.update({
where: { id: documentId },
data: { documentPath: null },
});
@@ -191,7 +191,7 @@ router.post('/customers/:id/business-registration', auth_js_1.authenticate, (0,
const customerId = parseInt(req.params.id);
const relativePath = `/uploads/business-registrations/${req.file.filename}`;
// Alte Datei löschen falls vorhanden
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
const customer = await prisma_js_1.default.customer.findUnique({ where: { id: customerId } });
if (customer?.businessRegistrationPath) {
const oldPath = path_1.default.join(process.cwd(), customer.businessRegistrationPath);
if (fs_1.default.existsSync(oldPath)) {
@@ -199,7 +199,7 @@ router.post('/customers/:id/business-registration', auth_js_1.authenticate, (0,
}
}
// Kunde in der DB aktualisieren
await prisma.customer.update({
await prisma_js_1.default.customer.update({
where: { id: customerId },
data: { businessRegistrationPath: relativePath },
});
@@ -228,7 +228,7 @@ router.post('/customers/:id/commercial-register', auth_js_1.authenticate, (0, au
const customerId = parseInt(req.params.id);
const relativePath = `/uploads/commercial-registers/${req.file.filename}`;
// Alte Datei löschen falls vorhanden
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
const customer = await prisma_js_1.default.customer.findUnique({ where: { id: customerId } });
if (customer?.commercialRegisterPath) {
const oldPath = path_1.default.join(process.cwd(), customer.commercialRegisterPath);
if (fs_1.default.existsSync(oldPath)) {
@@ -236,7 +236,7 @@ router.post('/customers/:id/commercial-register', auth_js_1.authenticate, (0, au
}
}
// Kunde in der DB aktualisieren
await prisma.customer.update({
await prisma_js_1.default.customer.update({
where: { id: customerId },
data: { commercialRegisterPath: relativePath },
});
@@ -259,7 +259,7 @@ router.post('/customers/:id/commercial-register', auth_js_1.authenticate, (0, au
router.delete('/customers/:id/business-registration', auth_js_1.authenticate, (0, auth_js_1.requirePermission)('customers:update'), async (req, res) => {
try {
const customerId = parseInt(req.params.id);
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
const customer = await prisma_js_1.default.customer.findUnique({ where: { id: customerId } });
if (!customer) {
res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
return;
@@ -274,7 +274,7 @@ router.delete('/customers/:id/business-registration', auth_js_1.authenticate, (0
fs_1.default.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.customer.update({
await prisma_js_1.default.customer.update({
where: { id: customerId },
data: { businessRegistrationPath: null },
});
@@ -289,7 +289,7 @@ router.delete('/customers/:id/business-registration', auth_js_1.authenticate, (0
router.delete('/customers/:id/commercial-register', auth_js_1.authenticate, (0, auth_js_1.requirePermission)('customers:update'), async (req, res) => {
try {
const customerId = parseInt(req.params.id);
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
const customer = await prisma_js_1.default.customer.findUnique({ where: { id: customerId } });
if (!customer) {
res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
return;
@@ -304,7 +304,7 @@ router.delete('/customers/:id/commercial-register', auth_js_1.authenticate, (0,
fs_1.default.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.customer.update({
await prisma_js_1.default.customer.update({
where: { id: customerId },
data: { commercialRegisterPath: null },
});
@@ -326,7 +326,7 @@ router.post('/customers/:id/privacy-policy', auth_js_1.authenticate, (0, auth_js
const customerId = parseInt(req.params.id);
const relativePath = `/uploads/privacy-policies/${req.file.filename}`;
// Alte Datei löschen falls vorhanden
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
const customer = await prisma_js_1.default.customer.findUnique({ where: { id: customerId } });
if (customer?.privacyPolicyPath) {
const oldPath = path_1.default.join(process.cwd(), customer.privacyPolicyPath);
if (fs_1.default.existsSync(oldPath)) {
@@ -334,19 +334,27 @@ router.post('/customers/:id/privacy-policy', auth_js_1.authenticate, (0, auth_js
}
}
// Kunde in der DB aktualisieren
await prisma.customer.update({
await prisma_js_1.default.customer.update({
where: { id: customerId },
data: { privacyPolicyPath: relativePath },
});
// Alle Consents auf GRANTED setzen (PDF = vollständige Einwilligung)
const consentTypes = ['DATA_PROCESSING', 'MARKETING_EMAIL', 'MARKETING_PHONE', 'DATA_SHARING_PARTNER'];
for (const consentType of consentTypes) {
await prisma.customerConsent.upsert({
await prisma_js_1.default.customerConsent.upsert({
where: { customerId_consentType: { customerId, consentType } },
update: { status: 'GRANTED', grantedAt: new Date(), source: 'papier' },
create: { customerId, consentType, status: 'GRANTED', grantedAt: new Date(), source: 'papier', createdBy: req.user?.email || 'admin' },
});
}
// Audit
const cust = await prisma_js_1.default.customer.findUnique({ where: { id: customerId }, select: { firstName: true, lastName: true } });
await (0, audit_service_js_1.logChange)({
req, action: 'CREATE', resourceType: 'CustomerConsent',
label: `Datenschutzerklärung-PDF hochgeladen für ${cust?.firstName} ${cust?.lastName} alle Einwilligungen erteilt`,
details: { aktion: 'PDF hochgeladen', einwilligungen: 'alle erteilt', quelle: 'papier' },
customerId,
});
res.json({
success: true,
data: {
@@ -366,7 +374,7 @@ router.post('/customers/:id/privacy-policy', auth_js_1.authenticate, (0, auth_js
router.delete('/customers/:id/privacy-policy', auth_js_1.authenticate, (0, auth_js_1.requirePermission)('customers:update'), async (req, res) => {
try {
const customerId = parseInt(req.params.id);
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
const customer = await prisma_js_1.default.customer.findUnique({ where: { id: customerId } });
if (!customer) {
res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
return;
@@ -381,15 +389,23 @@ router.delete('/customers/:id/privacy-policy', auth_js_1.authenticate, (0, auth_
fs_1.default.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.customer.update({
await prisma_js_1.default.customer.update({
where: { id: customerId },
data: { privacyPolicyPath: null },
});
// Nur Consents widerrufen die per Papier erteilt wurden
await prisma.customerConsent.updateMany({
await prisma_js_1.default.customerConsent.updateMany({
where: { customerId, status: 'GRANTED', source: 'papier' },
data: { status: 'WITHDRAWN', withdrawnAt: new Date() },
});
// Audit
const cust = await prisma_js_1.default.customer.findUnique({ where: { id: customerId }, select: { firstName: true, lastName: true } });
await (0, audit_service_js_1.logChange)({
req, action: 'DELETE', resourceType: 'CustomerConsent',
label: `Datenschutzerklärung-PDF gelöscht für ${cust?.firstName} ${cust?.lastName} Papier-Einwilligungen widerrufen`,
details: { aktion: 'PDF gelöscht', einwilligungen: 'papier-basierte widerrufen' },
customerId,
});
res.json({ success: true });
}
catch (error) {
@@ -408,7 +424,7 @@ async function handleContractDocumentUpload(req, res, fieldName, subDir) {
const contractId = parseInt(req.params.id);
const relativePath = `/uploads/${subDir}/${req.file.filename}`;
// Alte Datei löschen falls vorhanden
const contract = await prisma.contract.findUnique({ where: { id: contractId } });
const contract = await prisma_js_1.default.contract.findUnique({ where: { id: contractId } });
if (!contract) {
res.status(404).json({ success: false, error: 'Vertrag nicht gefunden' });
return;
@@ -421,7 +437,7 @@ async function handleContractDocumentUpload(req, res, fieldName, subDir) {
}
}
// Vertrag in der DB aktualisieren
await prisma.contract.update({
await prisma_js_1.default.contract.update({
where: { id: contractId },
data: { [fieldName]: relativePath },
});
@@ -444,7 +460,7 @@ async function handleContractDocumentUpload(req, res, fieldName, subDir) {
async function handleContractDocumentDelete(req, res, fieldName) {
try {
const contractId = parseInt(req.params.id);
const contract = await prisma.contract.findUnique({ where: { id: contractId } });
const contract = await prisma_js_1.default.contract.findUnique({ where: { id: contractId } });
if (!contract) {
res.status(404).json({ success: false, error: 'Vertrag nicht gefunden' });
return;
@@ -460,7 +476,7 @@ async function handleContractDocumentDelete(req, res, fieldName) {
fs_1.default.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.contract.update({
await prisma_js_1.default.contract.update({
where: { id: contractId },
data: { [fieldName]: null },
});
@@ -494,7 +510,7 @@ router.post('/invoices/:id', auth_js_1.authenticate, (0, auth_js_1.requirePermis
const invoiceId = parseInt(req.params.id);
const relativePath = `/uploads/invoices/${req.file.filename}`;
// Alte Datei löschen falls vorhanden
const invoice = await prisma.invoice.findUnique({ where: { id: invoiceId } });
const invoice = await prisma_js_1.default.invoice.findUnique({ where: { id: invoiceId } });
if (!invoice) {
res.status(404).json({ success: false, error: 'Rechnung nicht gefunden' });
return;
@@ -506,7 +522,7 @@ router.post('/invoices/:id', auth_js_1.authenticate, (0, auth_js_1.requirePermis
}
}
// Invoice in der DB aktualisieren
await prisma.invoice.update({
await prisma_js_1.default.invoice.update({
where: { id: invoiceId },
data: { documentPath: relativePath },
});
@@ -529,7 +545,7 @@ router.post('/invoices/:id', auth_js_1.authenticate, (0, auth_js_1.requirePermis
router.delete('/invoices/:id', auth_js_1.authenticate, (0, auth_js_1.requirePermission)('contracts:update'), async (req, res) => {
try {
const invoiceId = parseInt(req.params.id);
const invoice = await prisma.invoice.findUnique({ where: { id: invoiceId } });
const invoice = await prisma_js_1.default.invoice.findUnique({ where: { id: invoiceId } });
if (!invoice) {
res.status(404).json({ success: false, error: 'Rechnung nicht gefunden' });
return;
@@ -544,7 +560,7 @@ router.delete('/invoices/:id', auth_js_1.authenticate, (0, auth_js_1.requirePerm
fs_1.default.unlinkSync(filePath);
}
// documentPath in DB auf null setzen
await prisma.invoice.update({
await prisma_js_1.default.invoice.update({
where: { id: invoiceId },
data: { documentPath: null },
});
File diff suppressed because one or more lines are too long