first commit
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user