first commit

This commit is contained in:
Stefan Hacker
2026-01-29 01:16:54 +01:00
commit 31f807fbd0
12106 changed files with 2480685 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
import { Router } from 'express';
import * as customerController from '../controllers/customer.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.put('/:id', authenticate, requirePermission('customers:update'), customerController.updateAddress);
router.delete('/:id', authenticate, requirePermission('customers:delete'), customerController.deleteAddress);
export default router;
+29
View File
@@ -0,0 +1,29 @@
import { Router } from 'express';
import * as appSettingController from '../controllers/appSetting.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// Öffentliche Einstellungen (für alle authentifizierten Benutzer, inkl. Kunden)
router.get('/public', authenticate, appSettingController.getPublicSettings);
// Alle Einstellungen (nur Admin)
router.get('/', authenticate, requirePermission('settings:read'), appSettingController.getAllSettings);
// Einzelne Einstellung aktualisieren (nur Admin)
router.put(
'/:key',
authenticate,
requirePermission('settings:update'),
appSettingController.updateSetting
);
// Mehrere Einstellungen aktualisieren (nur Admin)
router.put(
'/',
authenticate,
requirePermission('settings:update'),
appSettingController.updateSettings
);
export default router;
+12
View File
@@ -0,0 +1,12 @@
import { Router } from 'express';
import * as authController from '../controllers/auth.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.post('/login', authController.login);
router.post('/customer-login', authController.customerLogin); // Kundenportal-Login
router.get('/me', authenticate, authController.me);
router.post('/register', authenticate, requirePermission('users:create'), authController.register);
export default router;
+10
View File
@@ -0,0 +1,10 @@
import { Router } from 'express';
import * as customerController from '../controllers/customer.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.put('/:id', authenticate, requirePermission('customers:update'), customerController.updateBankCard);
router.delete('/:id', authenticate, requirePermission('customers:delete'), customerController.deleteBankCard);
export default router;
@@ -0,0 +1,13 @@
import { Router } from 'express';
import * as cancellationPeriodController from '../controllers/cancellation-period.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.get('/', authenticate, cancellationPeriodController.getCancellationPeriods);
router.post('/', authenticate, requirePermission('platforms:create'), cancellationPeriodController.createCancellationPeriod);
router.get('/:id', authenticate, cancellationPeriodController.getCancellationPeriod);
router.put('/:id', authenticate, requirePermission('platforms:update'), cancellationPeriodController.updateCancellationPeriod);
router.delete('/:id', authenticate, requirePermission('platforms:delete'), cancellationPeriodController.deleteCancellationPeriod);
export default router;
@@ -0,0 +1,13 @@
import { Router } from 'express';
import * as contractDurationController from '../controllers/contract-duration.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.get('/', authenticate, contractDurationController.getContractDurations);
router.post('/', authenticate, requirePermission('platforms:create'), contractDurationController.createContractDuration);
router.get('/:id', authenticate, contractDurationController.getContractDuration);
router.put('/:id', authenticate, requirePermission('platforms:update'), contractDurationController.updateContractDuration);
router.delete('/:id', authenticate, requirePermission('platforms:delete'), contractDurationController.deleteContractDuration);
export default router;
+32
View File
@@ -0,0 +1,32 @@
import { Router } from 'express';
import * as contractController from '../controllers/contract.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.get('/', authenticate, requirePermission('contracts:read'), contractController.getContracts);
router.post('/', authenticate, requirePermission('contracts:create'), contractController.createContract);
// Vertrags-Cockpit (muss VOR /:id stehen!)
router.get('/cockpit', authenticate, requirePermission('contracts:read'), contractController.getCockpit);
router.get('/:id', authenticate, requirePermission('contracts:read'), contractController.getContract);
router.put('/:id', authenticate, requirePermission('contracts:update'), contractController.updateContract);
router.delete('/:id', authenticate, requirePermission('contracts:delete'), contractController.deleteContract);
// Follow-up contract
router.post('/:id/follow-up', authenticate, requirePermission('contracts:create'), contractController.createFollowUp);
// Get decrypted password
router.get('/:id/password', authenticate, requirePermission('contracts:read'), contractController.getContractPassword);
// Get decrypted SimCard PIN/PUK
router.get('/simcard/:simCardId/credentials', authenticate, requirePermission('contracts:read'), contractController.getSimCardCredentials);
// Get decrypted Internet password
router.get('/:id/internet-credentials', authenticate, requirePermission('contracts:read'), contractController.getInternetCredentials);
// Get decrypted SIP password
router.get('/phonenumber/:phoneNumberId/sip-credentials', authenticate, requirePermission('contracts:read'), contractController.getSipCredentials);
export default router;
@@ -0,0 +1,13 @@
import { Router } from 'express';
import * as contractCategoryController from '../controllers/contractCategory.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.get('/', authenticate, contractCategoryController.getContractCategories);
router.post('/', authenticate, requirePermission('platforms:create'), contractCategoryController.createContractCategory);
router.get('/:id', authenticate, contractCategoryController.getContractCategory);
router.put('/:id', authenticate, requirePermission('platforms:update'), contractCategoryController.updateContractCategory);
router.delete('/:id', authenticate, requirePermission('platforms:delete'), contractCategoryController.deleteContractCategory);
export default router;
+133
View File
@@ -0,0 +1,133 @@
import { Router } from 'express';
import * as contractTaskController from '../controllers/contractTask.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// ==================== ALL TASKS (vertragsübergreifend) ====================
// Alle Aufgaben über alle Verträge (für Dashboard & Task-Liste)
router.get(
'/tasks',
authenticate,
requirePermission('contracts:read'),
contractTaskController.getAllTasks
);
// Task-Statistik (offene Aufgaben)
router.get(
'/tasks/stats',
authenticate,
requirePermission('contracts:read'),
contractTaskController.getTaskStats
);
// ==================== TASKS BY CONTRACT ====================
// Alle Aufgaben eines Vertrags abrufen (auch für Kundenportal, aber nur sichtbare)
router.get(
'/contracts/:contractId/tasks',
authenticate,
requirePermission('contracts:read'),
contractTaskController.getTasks
);
// Neue Aufgabe erstellen (nur für Mitarbeiter mit contracts:update)
router.post(
'/contracts/:contractId/tasks',
authenticate,
requirePermission('contracts:update'),
contractTaskController.createTask
);
// Support-Anfrage erstellen (für Kundenportal-Benutzer, nur contracts:read erforderlich)
router.post(
'/contracts/:contractId/support-ticket',
authenticate,
requirePermission('contracts:read'),
contractTaskController.createSupportTicket
);
// Aufgabe aktualisieren
router.put(
'/tasks/:taskId',
authenticate,
requirePermission('contracts:update'),
contractTaskController.updateTask
);
// Aufgabe als erledigt markieren
router.post(
'/tasks/:taskId/complete',
authenticate,
requirePermission('contracts:update'),
contractTaskController.completeTask
);
// Aufgabe wieder öffnen
router.post(
'/tasks/:taskId/reopen',
authenticate,
requirePermission('contracts:update'),
contractTaskController.reopenTask
);
// Aufgabe löschen
router.delete(
'/tasks/:taskId',
authenticate,
requirePermission('contracts:delete'),
contractTaskController.deleteTask
);
// ==================== SUBTASKS ====================
// Neue Unteraufgabe erstellen (Mitarbeiter)
router.post(
'/tasks/:taskId/subtasks',
authenticate,
requirePermission('contracts:update'),
contractTaskController.createSubtask
);
// Antwort auf eigenes Ticket erstellen (Kundenportal)
router.post(
'/tasks/:taskId/reply',
authenticate,
requirePermission('contracts:read'),
contractTaskController.createCustomerReply
);
// Unteraufgabe als erledigt markieren
router.post(
'/subtasks/:subtaskId/complete',
authenticate,
requirePermission('contracts:update'),
contractTaskController.completeSubtask
);
// Unteraufgabe wieder öffnen
router.post(
'/subtasks/:subtaskId/reopen',
authenticate,
requirePermission('contracts:update'),
contractTaskController.reopenSubtask
);
// Unteraufgabe aktualisieren
router.put(
'/subtasks/:subtaskId',
authenticate,
requirePermission('contracts:update'),
contractTaskController.updateSubtask
);
// Unteraufgabe löschen
router.delete(
'/subtasks/:subtaskId',
authenticate,
requirePermission('contracts:delete'),
contractTaskController.deleteSubtask
);
export default router;
+47
View File
@@ -0,0 +1,47 @@
import { Router } from 'express';
import * as customerController from '../controllers/customer.controller.js';
import * as stressfreiEmailController from '../controllers/stressfreiEmail.controller.js';
import { authenticate, requirePermission, requireCustomerAccess } from '../middleware/auth.js';
const router = Router();
// Customers
router.get('/', authenticate, requirePermission('customers:read'), customerController.getCustomers);
router.post('/', authenticate, requirePermission('customers:create'), customerController.createCustomer);
router.get('/:id', authenticate, requireCustomerAccess, customerController.getCustomer);
router.put('/:id', authenticate, requirePermission('customers:update'), customerController.updateCustomer);
router.delete('/:id', authenticate, requirePermission('customers:delete'), customerController.deleteCustomer);
// Addresses
router.get('/:customerId/addresses', authenticate, requireCustomerAccess, customerController.getAddresses);
router.post('/:customerId/addresses', authenticate, requirePermission('customers:update'), customerController.createAddress);
// Bank Cards
router.get('/:customerId/bank-cards', authenticate, requireCustomerAccess, customerController.getBankCards);
router.post('/:customerId/bank-cards', authenticate, requirePermission('customers:update'), customerController.createBankCard);
// Identity Documents
router.get('/:customerId/documents', authenticate, requireCustomerAccess, customerController.getDocuments);
router.post('/:customerId/documents', authenticate, requirePermission('customers:update'), customerController.createDocument);
// Meters
router.get('/:customerId/meters', authenticate, requireCustomerAccess, customerController.getMeters);
router.post('/:customerId/meters', authenticate, requirePermission('customers:update'), customerController.createMeter);
// Stressfrei-Wechseln E-Mail-Adressen
router.get('/:customerId/stressfrei-emails', authenticate, requireCustomerAccess, stressfreiEmailController.getEmailsByCustomer);
router.post('/:customerId/stressfrei-emails', authenticate, requirePermission('customers:update'), stressfreiEmailController.createEmail);
// Portal Settings
router.get('/:customerId/portal', authenticate, requirePermission('customers:update'), customerController.getPortalSettings);
router.put('/:customerId/portal', authenticate, requirePermission('customers:update'), customerController.updatePortalSettings);
router.post('/:customerId/portal/password', authenticate, requirePermission('customers:update'), customerController.setPortalPassword);
router.get('/:customerId/portal/password', authenticate, requirePermission('customers:update'), customerController.getPortalPassword);
// Representatives (Vertreter)
router.get('/:customerId/representatives', authenticate, requirePermission('customers:read'), customerController.getRepresentatives);
router.post('/:customerId/representatives', authenticate, requirePermission('customers:update'), customerController.addRepresentative);
router.delete('/:customerId/representatives/:representativeId', authenticate, requirePermission('customers:update'), customerController.removeRepresentative);
router.get('/:customerId/representatives/search', authenticate, requirePermission('customers:read'), customerController.searchForRepresentative);
export default router;
+512
View File
@@ -0,0 +1,512 @@
import { Router, Response } from 'express';
import { PrismaClient, Prisma } from '@prisma/client';
import { authenticate, requirePermission } from '../middleware/auth.js';
import { AuthRequest } from '../types/index.js';
const router = Router();
const prisma = new 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: Response) => {
try {
// Create or get the developer:access permission
const developerPerm = await prisma.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({
where: { name: 'Admin' },
include: { permissions: true },
});
if (!adminRole) {
res.status(404).json({ success: false, error: 'Admin-Rolle nicht gefunden' });
return;
}
// Check if Admin already has this permission
const hasPermission = adminRole.permissions.some(
(rp) => rp.permissionId === developerPerm.id
);
if (!hasPermission) {
await prisma.rolePermission.create({
data: {
roleId: adminRole.id,
permissionId: developerPerm.id,
},
});
res.json({ success: true, message: 'developer:access Permission wurde zur Admin-Rolle hinzugefügt. Bitte neu einloggen!' });
} else {
res.json({ success: true, message: 'Admin-Rolle hat bereits die developer:access Permission' });
}
} catch (error) {
console.error('Setup error:', error);
res.status(500).json({ success: false, error: 'Fehler beim Setup' });
}
});
// Tabellen-Metadaten mit Beziehungen
const tableMetadata: Record<string, {
model: string;
primaryKey: string;
readonlyFields: string[];
requiredFields: string[];
relations: { field: string; targetTable: string; type: 'one' | 'many' }[];
foreignKeys: { field: string; targetTable: string }[];
}> = {
User: {
model: 'user',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt', 'password'],
requiredFields: ['email', 'firstName', 'lastName'],
relations: [
{ field: 'customer', targetTable: 'Customer', type: 'one' },
{ field: 'roles', targetTable: 'UserRole', type: 'many' },
],
foreignKeys: [{ field: 'customerId', targetTable: 'Customer' }],
},
Role: {
model: 'role',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt'],
requiredFields: ['name'],
relations: [
{ field: 'permissions', targetTable: 'RolePermission', type: 'many' },
{ field: 'users', targetTable: 'UserRole', type: 'many' },
],
foreignKeys: [],
},
Permission: {
model: 'permission',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['resource', 'action'],
relations: [{ field: 'roles', targetTable: 'RolePermission', type: 'many' }],
foreignKeys: [],
},
RolePermission: {
model: 'rolePermission',
primaryKey: 'roleId,permissionId',
readonlyFields: [],
requiredFields: ['roleId', 'permissionId'],
relations: [],
foreignKeys: [
{ field: 'roleId', targetTable: 'Role' },
{ field: 'permissionId', targetTable: 'Permission' },
],
},
UserRole: {
model: 'userRole',
primaryKey: 'userId,roleId',
readonlyFields: [],
requiredFields: ['userId', 'roleId'],
relations: [],
foreignKeys: [
{ field: 'userId', targetTable: 'User' },
{ field: 'roleId', targetTable: 'Role' },
],
},
Customer: {
model: 'customer',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt', 'customerNumber'],
requiredFields: ['firstName', 'lastName'],
relations: [
{ field: 'user', targetTable: 'User', type: 'one' },
{ field: 'addresses', targetTable: 'Address', type: 'many' },
{ field: 'bankCards', targetTable: 'BankCard', type: 'many' },
{ field: 'identityDocuments', targetTable: 'IdentityDocument', type: 'many' },
{ field: 'meters', targetTable: 'Meter', type: 'many' },
{ field: 'contracts', targetTable: 'Contract', type: 'many' },
],
foreignKeys: [],
},
Address: {
model: 'address',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt'],
requiredFields: ['customerId', 'street', 'houseNumber', 'postalCode', 'city'],
relations: [
{ field: 'customer', targetTable: 'Customer', type: 'one' },
{ field: 'contracts', targetTable: 'Contract', type: 'many' },
],
foreignKeys: [{ field: 'customerId', targetTable: 'Customer' }],
},
BankCard: {
model: 'bankCard',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt'],
requiredFields: ['customerId', 'accountHolder', 'iban'],
relations: [
{ field: 'customer', targetTable: 'Customer', type: 'one' },
{ field: 'contracts', targetTable: 'Contract', type: 'many' },
],
foreignKeys: [{ field: 'customerId', targetTable: 'Customer' }],
},
IdentityDocument: {
model: 'identityDocument',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt'],
requiredFields: ['customerId', 'documentNumber'],
relations: [
{ field: 'customer', targetTable: 'Customer', type: 'one' },
{ field: 'contracts', targetTable: 'Contract', type: 'many' },
],
foreignKeys: [{ field: 'customerId', targetTable: 'Customer' }],
},
Meter: {
model: 'meter',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt'],
requiredFields: ['customerId', 'meterNumber', 'type'],
relations: [
{ field: 'customer', targetTable: 'Customer', type: 'one' },
{ field: 'readings', targetTable: 'MeterReading', type: 'many' },
{ field: 'energyDetails', targetTable: 'EnergyContractDetails', type: 'many' },
],
foreignKeys: [{ field: 'customerId', targetTable: 'Customer' }],
},
MeterReading: {
model: 'meterReading',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt'],
requiredFields: ['meterId', 'readingDate', 'value'],
relations: [{ field: 'meter', targetTable: 'Meter', type: 'one' }],
foreignKeys: [{ field: 'meterId', targetTable: 'Meter' }],
},
SalesPlatform: {
model: 'salesPlatform',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt'],
requiredFields: ['name'],
relations: [{ field: 'contracts', targetTable: 'Contract', type: 'many' }],
foreignKeys: [],
},
Contract: {
model: 'contract',
primaryKey: 'id',
readonlyFields: ['id', 'createdAt', 'updatedAt', 'contractNumber'],
requiredFields: ['customerId', 'type'],
relations: [
{ field: 'customer', targetTable: 'Customer', type: 'one' },
{ field: 'address', targetTable: 'Address', type: 'one' },
{ field: 'bankCard', targetTable: 'BankCard', type: 'one' },
{ field: 'identityDocument', targetTable: 'IdentityDocument', type: 'one' },
{ field: 'salesPlatform', targetTable: 'SalesPlatform', type: 'one' },
{ field: 'previousContract', targetTable: 'Contract', type: 'one' },
{ field: 'followUpContract', targetTable: 'Contract', type: 'one' },
{ field: 'energyDetails', targetTable: 'EnergyContractDetails', type: 'one' },
{ field: 'internetDetails', targetTable: 'InternetContractDetails', type: 'one' },
{ field: 'mobileDetails', targetTable: 'MobileContractDetails', type: 'one' },
{ field: 'tvDetails', targetTable: 'TvContractDetails', type: 'one' },
{ field: 'carInsuranceDetails', targetTable: 'CarInsuranceDetails', type: 'one' },
],
foreignKeys: [
{ field: 'customerId', targetTable: 'Customer' },
{ field: 'addressId', targetTable: 'Address' },
{ field: 'bankCardId', targetTable: 'BankCard' },
{ field: 'identityDocumentId', targetTable: 'IdentityDocument' },
{ field: 'salesPlatformId', targetTable: 'SalesPlatform' },
{ field: 'previousContractId', targetTable: 'Contract' },
],
},
EnergyContractDetails: {
model: 'energyContractDetails',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['contractId'],
relations: [
{ field: 'contract', targetTable: 'Contract', type: 'one' },
{ field: 'meter', targetTable: 'Meter', type: 'one' },
],
foreignKeys: [
{ field: 'contractId', targetTable: 'Contract' },
{ field: 'meterId', targetTable: 'Meter' },
],
},
InternetContractDetails: {
model: 'internetContractDetails',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['contractId'],
relations: [
{ field: 'contract', targetTable: 'Contract', type: 'one' },
{ field: 'phoneNumbers', targetTable: 'PhoneNumber', type: 'many' },
],
foreignKeys: [{ field: 'contractId', targetTable: 'Contract' }],
},
PhoneNumber: {
model: 'phoneNumber',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['internetContractDetailsId', 'phoneNumber'],
relations: [{ field: 'internetDetails', targetTable: 'InternetContractDetails', type: 'one' }],
foreignKeys: [{ field: 'internetContractDetailsId', targetTable: 'InternetContractDetails' }],
},
MobileContractDetails: {
model: 'mobileContractDetails',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['contractId'],
relations: [{ field: 'contract', targetTable: 'Contract', type: 'one' }],
foreignKeys: [{ field: 'contractId', targetTable: 'Contract' }],
},
TvContractDetails: {
model: 'tvContractDetails',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['contractId'],
relations: [{ field: 'contract', targetTable: 'Contract', type: 'one' }],
foreignKeys: [{ field: 'contractId', targetTable: 'Contract' }],
},
CarInsuranceDetails: {
model: 'carInsuranceDetails',
primaryKey: 'id',
readonlyFields: ['id'],
requiredFields: ['contractId'],
relations: [{ field: 'contract', targetTable: 'Contract', type: 'one' }],
foreignKeys: [{ field: 'contractId', targetTable: 'Contract' }],
},
};
// Schema-Informationen abrufen
router.get(
'/schema',
authenticate,
requirePermission('developer:access'),
async (req: AuthRequest, res: Response) => {
try {
const tables = Object.entries(tableMetadata).map(([name, meta]) => ({
name,
...meta,
}));
res.json({ success: true, data: tables });
} catch (error) {
console.error('Schema error:', error);
res.status(500).json({ success: false, error: 'Fehler beim Laden des Schemas' });
}
}
);
// Tabellen-Daten abrufen
router.get(
'/table/:tableName',
authenticate,
requirePermission('developer:access'),
async (req: AuthRequest, res: Response) => {
try {
const { tableName } = req.params;
const { page = '1', limit = '50' } = req.query;
const meta = tableMetadata[tableName];
if (!meta) {
res.status(404).json({ success: false, error: 'Tabelle nicht gefunden' });
return;
}
const skip = (parseInt(page as string) - 1) * parseInt(limit as string);
const take = parseInt(limit as string);
const model = (prisma as any)[meta.model];
const [data, total] = await Promise.all([
model.findMany({
skip,
take,
orderBy: meta.primaryKey.includes(',') ? undefined : { [meta.primaryKey.split(',')[0]]: 'desc' },
}),
model.count(),
]);
res.json({
success: true,
data,
meta: {
...meta,
tableName,
},
pagination: {
page: parseInt(page as string),
limit: parseInt(limit as string),
total,
totalPages: Math.ceil(total / parseInt(limit as string)),
},
});
} catch (error) {
console.error('Table data error:', error);
res.status(500).json({ success: false, error: 'Fehler beim Laden der Daten' });
}
}
);
// Einzelne Zeile aktualisieren
router.put(
'/table/:tableName/:id',
authenticate,
requirePermission('developer:access'),
async (req: AuthRequest, res: Response) => {
try {
const { tableName, id } = req.params;
const updates = req.body;
const meta = tableMetadata[tableName];
if (!meta) {
res.status(404).json({ success: false, error: 'Tabelle nicht gefunden' });
return;
}
// Readonly-Felder aus Updates entfernen
const filteredUpdates: Record<string, any> = {};
for (const [key, value] of Object.entries(updates)) {
if (!meta.readonlyFields.includes(key)) {
filteredUpdates[key] = value;
}
}
// Prüfen ob required-Felder nicht auf null/leer gesetzt werden
for (const field of meta.requiredFields) {
if (field in filteredUpdates && (filteredUpdates[field] === null || filteredUpdates[field] === '')) {
res.status(400).json({ success: false, error: `Feld '${field}' ist erforderlich` });
return;
}
}
const model = (prisma as any)[meta.model];
// Composite Primary Key Handling
let where: any;
if (meta.primaryKey.includes(',')) {
const keys = meta.primaryKey.split(',');
const idParts = id.split('-');
where = {};
keys.forEach((key, idx) => {
where[key] = parseInt(idParts[idx]);
});
} else {
where = { [meta.primaryKey]: parseInt(id) };
}
const updated = await model.update({
where,
data: filteredUpdates,
});
res.json({ success: true, data: updated });
} catch (error: any) {
console.error('Update error:', error);
if (error.code === 'P2003') {
res.status(400).json({ success: false, error: 'Fremdschlüssel-Verletzung: Referenzierter Datensatz existiert nicht' });
} else if (error.code === 'P2002') {
res.status(400).json({ success: false, error: 'Unique-Constraint-Verletzung: Wert existiert bereits' });
} else {
res.status(500).json({ success: false, error: 'Fehler beim Aktualisieren' });
}
}
}
);
// Zeile löschen (nur wenn keine abhängigen Daten)
router.delete(
'/table/:tableName/:id',
authenticate,
requirePermission('developer:access'),
async (req: AuthRequest, res: Response) => {
try {
const { tableName, id } = req.params;
const meta = tableMetadata[tableName];
if (!meta) {
res.status(404).json({ success: false, error: 'Tabelle nicht gefunden' });
return;
}
const model = (prisma as any)[meta.model];
// Composite Primary Key Handling
let where: any;
if (meta.primaryKey.includes(',')) {
const keys = meta.primaryKey.split(',');
const idParts = id.split('-');
where = {};
keys.forEach((key, idx) => {
where[key] = parseInt(idParts[idx]);
});
} else {
where = { [meta.primaryKey]: parseInt(id) };
}
// Prüfen ob abhängige Daten existieren (nur "many"-Relations)
const record = await model.findUnique({
where,
include: meta.relations
.filter((r) => r.type === 'many')
.reduce((acc, r) => ({ ...acc, [r.field]: { take: 1 } }), {}),
});
if (!record) {
res.status(404).json({ success: false, error: 'Datensatz nicht gefunden' });
return;
}
// Prüfen auf abhängige Daten
for (const rel of meta.relations.filter((r) => r.type === 'many')) {
if (record[rel.field] && record[rel.field].length > 0) {
res.status(400).json({
success: false,
error: `Kann nicht gelöscht werden: Es existieren abhängige Daten in '${rel.targetTable}'`,
});
return;
}
}
await model.delete({ where });
res.json({ success: true });
} catch (error: any) {
console.error('Delete error:', error);
if (error.code === 'P2003') {
res.status(400).json({ success: false, error: 'Kann nicht gelöscht werden: Fremdschlüssel-Abhängigkeit' });
} else {
res.status(500).json({ success: false, error: 'Fehler beim Löschen' });
}
}
}
);
// Referenzierte Daten für Dropdowns abrufen
router.get(
'/reference/:tableName',
authenticate,
requirePermission('developer:access'),
async (req: AuthRequest, res: Response) => {
try {
const { tableName } = req.params;
const { search = '', limit = '50' } = req.query;
const meta = tableMetadata[tableName];
if (!meta) {
res.status(404).json({ success: false, error: 'Tabelle nicht gefunden' });
return;
}
const model = (prisma as any)[meta.model];
const data = await model.findMany({
take: parseInt(limit as string),
});
res.json({ success: true, data });
} catch (error) {
console.error('Reference error:', error);
res.status(500).json({ success: false, error: 'Fehler beim Laden der Referenzdaten' });
}
}
);
export default router;
+10
View File
@@ -0,0 +1,10 @@
import { Router } from 'express';
import * as customerController from '../controllers/customer.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.put('/:id', authenticate, requirePermission('customers:update'), customerController.updateDocument);
router.delete('/:id', authenticate, requirePermission('customers:delete'), customerController.deleteDocument);
export default router;
@@ -0,0 +1,23 @@
// ==================== EMAIL PROVIDER ROUTES ====================
import { Router } from 'express';
import * as emailProviderController from '../controllers/emailProvider.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// Provider Config CRUD (Admin-only)
router.get('/configs', authenticate, requirePermission('settings:read'), emailProviderController.getProviderConfigs);
router.get('/configs/:id', authenticate, requirePermission('settings:read'), emailProviderController.getProviderConfig);
router.post('/configs', authenticate, requirePermission('settings:update'), emailProviderController.createProviderConfig);
router.put('/configs/:id', authenticate, requirePermission('settings:update'), emailProviderController.updateProviderConfig);
router.delete('/configs/:id', authenticate, requirePermission('settings:update'), emailProviderController.deleteProviderConfig);
// Email Operations
router.post('/test-connection', authenticate, requirePermission('settings:update'), emailProviderController.testConnection);
router.get('/domain', authenticate, emailProviderController.getProviderDomain);
router.get('/check/:localPart', authenticate, requirePermission('customers:read'), emailProviderController.checkEmailExists);
router.post('/provision', authenticate, requirePermission('customers:update'), emailProviderController.provisionEmail);
router.delete('/deprovision/:localPart', authenticate, requirePermission('customers:update'), emailProviderController.deprovisionEmail);
export default router;
+16
View File
@@ -0,0 +1,16 @@
import { Router } from 'express';
import * as customerController from '../controllers/customer.controller.js';
import { authenticate, requirePermission, requireCustomerAccess } from '../middleware/auth.js';
const router = Router();
router.put('/:id', authenticate, requirePermission('customers:update'), customerController.updateMeter);
router.delete('/:id', authenticate, requirePermission('customers:delete'), customerController.deleteMeter);
// Meter readings
router.get('/:meterId/readings', authenticate, customerController.getMeterReadings);
router.post('/:meterId/readings', authenticate, requirePermission('customers:update'), customerController.addMeterReading);
router.put('/:meterId/readings/:readingId', authenticate, requirePermission('customers:update'), customerController.updateMeterReading);
router.delete('/:meterId/readings/:readingId', authenticate, requirePermission('customers:delete'), customerController.deleteMeterReading);
export default router;
+13
View File
@@ -0,0 +1,13 @@
import { Router } from 'express';
import * as platformController from '../controllers/platform.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
router.get('/', authenticate, platformController.getPlatforms);
router.post('/', authenticate, requirePermission('platforms:create'), platformController.createPlatform);
router.get('/:id', authenticate, platformController.getPlatform);
router.put('/:id', authenticate, requirePermission('platforms:update'), platformController.updatePlatform);
router.delete('/:id', authenticate, requirePermission('platforms:delete'), platformController.deletePlatform);
export default router;
+19
View File
@@ -0,0 +1,19 @@
import { Router } from 'express';
import * as providerController from '../controllers/provider.controller.js';
import * as tariffController from '../controllers/tariff.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// Provider routes
router.get('/', authenticate, providerController.getProviders);
router.post('/', authenticate, requirePermission('providers:create'), providerController.createProvider);
router.get('/:id', authenticate, providerController.getProvider);
router.put('/:id', authenticate, requirePermission('providers:update'), providerController.updateProvider);
router.delete('/:id', authenticate, requirePermission('providers:delete'), providerController.deleteProvider);
// Nested tariff routes
router.get('/:providerId/tariffs', authenticate, tariffController.getTariffs);
router.post('/:providerId/tariffs', authenticate, requirePermission('providers:create'), tariffController.createTariff);
export default router;
@@ -0,0 +1,12 @@
import { Router } from 'express';
import * as stressfreiEmailController from '../controllers/stressfreiEmail.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// Einzelne Stressfrei-Email verwalten
router.get('/:id', authenticate, requirePermission('customers:read'), stressfreiEmailController.getEmail);
router.put('/:id', authenticate, requirePermission('customers:update'), stressfreiEmailController.updateEmail);
router.delete('/:id', authenticate, requirePermission('customers:delete'), stressfreiEmailController.deleteEmail);
export default router;
+12
View File
@@ -0,0 +1,12 @@
import { Router } from 'express';
import * as tariffController from '../controllers/tariff.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// Standalone tariff routes (for update/delete by tariff id)
router.get('/:id', authenticate, tariffController.getTariff);
router.put('/:id', authenticate, requirePermission('providers:update'), tariffController.updateTariff);
router.delete('/:id', authenticate, requirePermission('providers:delete'), tariffController.deleteTariff);
export default router;
+661
View File
@@ -0,0 +1,661 @@
import { Router, Response } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import { PrismaClient } from '@prisma/client';
import { authenticate, requirePermission } from '../middleware/auth.js';
import { AuthRequest } from '../types/index.js';
const router = Router();
const prisma = new PrismaClient();
// Uploads-Verzeichnis erstellen falls nicht vorhanden
const uploadsDir = path.join(process.cwd(), 'uploads');
if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir, { recursive: true });
}
// Multer-Konfiguration
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const subDir = (req as any).uploadSubDir || 'misc';
const targetDir = path.join(uploadsDir, subDir);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
cb(null, targetDir);
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
const ext = path.extname(file.originalname);
cb(null, `${uniqueSuffix}${ext}`);
},
});
const fileFilter = (
req: Express.Request,
file: Express.Multer.File,
cb: multer.FileFilterCallback
) => {
// Nur PDFs und Bilder erlauben
const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png', 'image/jpg'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Nur PDF, JPG und PNG Dateien sind erlaubt'));
}
};
const upload = multer({
storage,
fileFilter,
limits: {
fileSize: 10 * 1024 * 1024, // 10MB max
},
});
// Middleware um Subdirectory zu setzen
function setUploadDir(subDir: string) {
return (req: AuthRequest, res: Response, next: Function) => {
(req as any).uploadSubDir = subDir;
next();
};
}
// Upload für Bankkarten-Dokumente
router.post(
'/bank-cards/:id',
authenticate,
requirePermission('customers:update'),
setUploadDir('bank-cards'),
upload.single('document'),
async (req: AuthRequest, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
const bankCardId = parseInt(req.params.id);
const relativePath = `/uploads/bank-cards/${req.file.filename}`;
// Bankkarte in der DB aktualisieren
await prisma.bankCard.update({
where: { id: bankCardId },
data: { documentPath: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
);
// Upload für Ausweis-Dokumente
router.post(
'/documents/:id',
authenticate,
requirePermission('customers:update'),
setUploadDir('documents'),
upload.single('document'),
async (req: AuthRequest, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
const documentId = parseInt(req.params.id);
const relativePath = `/uploads/documents/${req.file.filename}`;
// Ausweis in der DB aktualisieren
await prisma.identityDocument.update({
where: { id: documentId },
data: { documentPath: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
);
// Löschen von Bankkarten-Dokumenten
router.delete(
'/bank-cards/:id',
authenticate,
requirePermission('customers:update'),
async (req: AuthRequest, res: Response) => {
try {
const bankCardId = parseInt(req.params.id);
// Bankkarte aus DB holen um Dateipfad zu bekommen
const bankCard = await prisma.bankCard.findUnique({
where: { id: bankCardId },
});
if (!bankCard) {
res.status(404).json({ success: false, error: 'Bankkarte nicht gefunden' });
return;
}
if (!bankCard.documentPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei von Festplatte löschen
const filePath = path.join(process.cwd(), bankCard.documentPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// documentPath in DB auf null setzen
await prisma.bankCard.update({
where: { id: bankCardId },
data: { documentPath: null },
});
res.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
);
// Löschen von Ausweis-Dokumenten
router.delete(
'/documents/:id',
authenticate,
requirePermission('customers:update'),
async (req: AuthRequest, res: Response) => {
try {
const documentId = parseInt(req.params.id);
// Ausweis aus DB holen um Dateipfad zu bekommen
const document = await prisma.identityDocument.findUnique({
where: { id: documentId },
});
if (!document) {
res.status(404).json({ success: false, error: 'Ausweis nicht gefunden' });
return;
}
if (!document.documentPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei von Festplatte löschen
const filePath = path.join(process.cwd(), document.documentPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// documentPath in DB auf null setzen
await prisma.identityDocument.update({
where: { id: documentId },
data: { documentPath: null },
});
res.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
);
// ==================== FIRMEN-DOKUMENTE ====================
// Upload für Gewerbeanmeldung
router.post(
'/customers/:id/business-registration',
authenticate,
requirePermission('customers:update'),
setUploadDir('business-registrations'),
upload.single('document'),
async (req: AuthRequest, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
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 } });
if (customer?.businessRegistrationPath) {
const oldPath = path.join(process.cwd(), customer.businessRegistrationPath);
if (fs.existsSync(oldPath)) {
fs.unlinkSync(oldPath);
}
}
// Kunde in der DB aktualisieren
await prisma.customer.update({
where: { id: customerId },
data: { businessRegistrationPath: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
);
// Upload für Handelsregisterauszug
router.post(
'/customers/:id/commercial-register',
authenticate,
requirePermission('customers:update'),
setUploadDir('commercial-registers'),
upload.single('document'),
async (req: AuthRequest, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
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 } });
if (customer?.commercialRegisterPath) {
const oldPath = path.join(process.cwd(), customer.commercialRegisterPath);
if (fs.existsSync(oldPath)) {
fs.unlinkSync(oldPath);
}
}
// Kunde in der DB aktualisieren
await prisma.customer.update({
where: { id: customerId },
data: { commercialRegisterPath: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
);
// Löschen der Gewerbeanmeldung
router.delete(
'/customers/:id/business-registration',
authenticate,
requirePermission('customers:update'),
async (req: AuthRequest, res: Response) => {
try {
const customerId = parseInt(req.params.id);
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
if (!customer) {
res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
return;
}
if (!customer.businessRegistrationPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei löschen
const filePath = path.join(process.cwd(), customer.businessRegistrationPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.customer.update({
where: { id: customerId },
data: { businessRegistrationPath: null },
});
res.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
);
// Löschen des Handelsregisterauszugs
router.delete(
'/customers/:id/commercial-register',
authenticate,
requirePermission('customers:update'),
async (req: AuthRequest, res: Response) => {
try {
const customerId = parseInt(req.params.id);
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
if (!customer) {
res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
return;
}
if (!customer.commercialRegisterPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei löschen
const filePath = path.join(process.cwd(), customer.commercialRegisterPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.customer.update({
where: { id: customerId },
data: { commercialRegisterPath: null },
});
res.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
);
// ==================== DATENSCHUTZERKLÄRUNG (für alle Kunden) ====================
// Upload für Datenschutzerklärung
router.post(
'/customers/:id/privacy-policy',
authenticate,
requirePermission('customers:update'),
setUploadDir('privacy-policies'),
upload.single('document'),
async (req: AuthRequest, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
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 } });
if (customer?.privacyPolicyPath) {
const oldPath = path.join(process.cwd(), customer.privacyPolicyPath);
if (fs.existsSync(oldPath)) {
fs.unlinkSync(oldPath);
}
}
// Kunde in der DB aktualisieren
await prisma.customer.update({
where: { id: customerId },
data: { privacyPolicyPath: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
);
// Löschen der Datenschutzerklärung
router.delete(
'/customers/:id/privacy-policy',
authenticate,
requirePermission('customers:update'),
async (req: AuthRequest, res: Response) => {
try {
const customerId = parseInt(req.params.id);
const customer = await prisma.customer.findUnique({ where: { id: customerId } });
if (!customer) {
res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
return;
}
if (!customer.privacyPolicyPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei löschen
const filePath = path.join(process.cwd(), customer.privacyPolicyPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.customer.update({
where: { id: customerId },
data: { privacyPolicyPath: null },
});
res.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
);
// ==================== VERTRAGS-DOKUMENTE ====================
// Generische Funktion für Vertrags-Dokument Upload
async function handleContractDocumentUpload(
req: AuthRequest,
res: Response,
fieldName: 'cancellationLetterPath' | 'cancellationConfirmationPath' | 'cancellationLetterOptionsPath' | 'cancellationConfirmationOptionsPath',
subDir: string
) {
try {
if (!req.file) {
res.status(400).json({ success: false, error: 'Keine Datei hochgeladen' });
return;
}
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 } });
if (!contract) {
res.status(404).json({ success: false, error: 'Vertrag nicht gefunden' });
return;
}
const oldPath = contract[fieldName];
if (oldPath) {
const fullPath = path.join(process.cwd(), oldPath);
if (fs.existsSync(fullPath)) {
fs.unlinkSync(fullPath);
}
}
// Vertrag in der DB aktualisieren
await prisma.contract.update({
where: { id: contractId },
data: { [fieldName]: relativePath },
});
res.json({
success: true,
data: {
path: relativePath,
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size,
},
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ success: false, error: 'Upload fehlgeschlagen' });
}
}
// Generische Funktion für Vertrags-Dokument Löschen
async function handleContractDocumentDelete(
req: AuthRequest,
res: Response,
fieldName: 'cancellationLetterPath' | 'cancellationConfirmationPath' | 'cancellationLetterOptionsPath' | 'cancellationConfirmationOptionsPath'
) {
try {
const contractId = parseInt(req.params.id);
const contract = await prisma.contract.findUnique({ where: { id: contractId } });
if (!contract) {
res.status(404).json({ success: false, error: 'Vertrag nicht gefunden' });
return;
}
const documentPath = contract[fieldName];
if (!documentPath) {
res.status(400).json({ success: false, error: 'Kein Dokument vorhanden' });
return;
}
// Datei löschen
const filePath = path.join(process.cwd(), documentPath);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
// Pfad in DB auf null setzen
await prisma.contract.update({
where: { id: contractId },
data: { [fieldName]: null },
});
res.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
res.status(500).json({ success: false, error: 'Löschen fehlgeschlagen' });
}
}
// Kündigungsschreiben
router.post(
'/contracts/:id/cancellation-letter',
authenticate,
requirePermission('contracts:update'),
setUploadDir('cancellation-letters'),
upload.single('document'),
(req: AuthRequest, res: Response) => handleContractDocumentUpload(req, res, 'cancellationLetterPath', 'cancellation-letters')
);
router.delete(
'/contracts/:id/cancellation-letter',
authenticate,
requirePermission('contracts:update'),
(req: AuthRequest, res: Response) => handleContractDocumentDelete(req, res, 'cancellationLetterPath')
);
// Kündigungsbestätigung
router.post(
'/contracts/:id/cancellation-confirmation',
authenticate,
requirePermission('contracts:update'),
setUploadDir('cancellation-confirmations'),
upload.single('document'),
(req: AuthRequest, res: Response) => handleContractDocumentUpload(req, res, 'cancellationConfirmationPath', 'cancellation-confirmations')
);
router.delete(
'/contracts/:id/cancellation-confirmation',
authenticate,
requirePermission('contracts:update'),
(req: AuthRequest, res: Response) => handleContractDocumentDelete(req, res, 'cancellationConfirmationPath')
);
// Kündigungsschreiben Optionen
router.post(
'/contracts/:id/cancellation-letter-options',
authenticate,
requirePermission('contracts:update'),
setUploadDir('cancellation-letters-options'),
upload.single('document'),
(req: AuthRequest, res: Response) => handleContractDocumentUpload(req, res, 'cancellationLetterOptionsPath', 'cancellation-letters-options')
);
router.delete(
'/contracts/:id/cancellation-letter-options',
authenticate,
requirePermission('contracts:update'),
(req: AuthRequest, res: Response) => handleContractDocumentDelete(req, res, 'cancellationLetterOptionsPath')
);
// Kündigungsbestätigung Optionen
router.post(
'/contracts/:id/cancellation-confirmation-options',
authenticate,
requirePermission('contracts:update'),
setUploadDir('cancellation-confirmations-options'),
upload.single('document'),
(req: AuthRequest, res: Response) => handleContractDocumentUpload(req, res, 'cancellationConfirmationOptionsPath', 'cancellation-confirmations-options')
);
router.delete(
'/contracts/:id/cancellation-confirmation-options',
authenticate,
requirePermission('contracts:update'),
(req: AuthRequest, res: Response) => handleContractDocumentDelete(req, res, 'cancellationConfirmationOptionsPath')
);
export default router;
+24
View File
@@ -0,0 +1,24 @@
import { Router } from 'express';
import * as userController from '../controllers/user.controller.js';
import { authenticate, requirePermission } from '../middleware/auth.js';
const router = Router();
// Users (Admin only)
router.get('/', authenticate, requirePermission('users:read'), userController.getUsers);
router.post('/', authenticate, requirePermission('users:create'), userController.createUser);
router.get('/:id', authenticate, requirePermission('users:read'), userController.getUser);
router.put('/:id', authenticate, requirePermission('users:update'), userController.updateUser);
router.delete('/:id', authenticate, requirePermission('users:delete'), userController.deleteUser);
// Roles
router.get('/roles/list', authenticate, requirePermission('users:read'), userController.getRoles);
router.post('/roles', authenticate, requirePermission('users:create'), userController.createRole);
router.get('/roles/:id', authenticate, requirePermission('users:read'), userController.getRole);
router.put('/roles/:id', authenticate, requirePermission('users:update'), userController.updateRole);
router.delete('/roles/:id', authenticate, requirePermission('users:delete'), userController.deleteRole);
// Permissions
router.get('/permissions/list', authenticate, requirePermission('users:read'), userController.getPermissions);
export default router;