Implement navigation and view management in DataViewer, adding Dashboard and TableManagement components. Update export data handling based on current view. Create new components for managing Kreditor, Konto, and BU tables with CRUD functionality. Refactor admin routes to remove admin access checks and streamline data handling for various entities.
This commit is contained in:
@@ -1,110 +1,257 @@
|
||||
const express = require('express');
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
const { checkAuthorizedEmail } = require('../middleware/emailAuth');
|
||||
const { executeQuery, sql } = require('../config/database');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Check if user is admin (first email in the list or specific admin email)
|
||||
const checkAdminAccess = (req, res, next) => {
|
||||
const authorizedEmails = process.env.AUTHORIZED_EMAILS;
|
||||
if (!authorizedEmails || authorizedEmails.trim() === '') {
|
||||
return res.status(403).json({ error: 'No authorized emails configured' });
|
||||
}
|
||||
|
||||
const emailList = authorizedEmails.split(',').map(email => email.trim().toLowerCase());
|
||||
const userEmail = req.user?.email?.toLowerCase();
|
||||
|
||||
// First email in the list is considered admin, or check for specific admin emails
|
||||
const adminEmails = [emailList[0]]; // First email is admin
|
||||
|
||||
if (!adminEmails.includes(userEmail)) {
|
||||
return res.status(403).json({
|
||||
error: 'Admin access required',
|
||||
message: 'Only administrators can access this resource'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
// Removed admin access check - all authenticated users can access these routes
|
||||
|
||||
// Get current authorized emails (admin only)
|
||||
router.get('/authorized-emails', authenticateToken, checkAdminAccess, (req, res) => {
|
||||
const authorizedEmails = process.env.AUTHORIZED_EMAILS;
|
||||
if (!authorizedEmails) {
|
||||
return res.json({ emails: [] });
|
||||
}
|
||||
|
||||
const emailList = authorizedEmails.split(',').map(email => email.trim());
|
||||
res.json({ emails: emailList });
|
||||
});
|
||||
|
||||
// Add authorized email (admin only)
|
||||
router.post('/authorized-emails', authenticateToken, checkAdminAccess, (req, res) => {
|
||||
const { email } = req.body;
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
return res.status(400).json({ error: 'Valid email address required' });
|
||||
}
|
||||
|
||||
const authorizedEmails = process.env.AUTHORIZED_EMAILS || '';
|
||||
const emailList = authorizedEmails.split(',').map(e => e.trim()).filter(e => e);
|
||||
|
||||
const newEmail = email.trim().toLowerCase();
|
||||
if (emailList.map(e => e.toLowerCase()).includes(newEmail)) {
|
||||
return res.status(400).json({ error: 'Email already authorized' });
|
||||
}
|
||||
|
||||
emailList.push(email.trim());
|
||||
|
||||
// Note: This only updates the runtime environment variable
|
||||
// For persistent changes, you'd need to update the .env file
|
||||
process.env.AUTHORIZED_EMAILS = emailList.join(',');
|
||||
|
||||
res.json({
|
||||
message: 'Email added successfully',
|
||||
emails: emailList,
|
||||
note: 'Changes are temporary. Update .env file for permanent changes.'
|
||||
});
|
||||
});
|
||||
|
||||
// Remove authorized email (admin only)
|
||||
router.delete('/authorized-emails/:email', authenticateToken, checkAdminAccess, (req, res) => {
|
||||
const emailToRemove = req.params.email.toLowerCase();
|
||||
const authorizedEmails = process.env.AUTHORIZED_EMAILS || '';
|
||||
const emailList = authorizedEmails.split(',').map(e => e.trim()).filter(e => e);
|
||||
|
||||
const filteredEmails = emailList.filter(email => email.toLowerCase() !== emailToRemove);
|
||||
|
||||
if (filteredEmails.length === emailList.length) {
|
||||
return res.status(404).json({ error: 'Email not found in authorized list' });
|
||||
}
|
||||
|
||||
// Don't allow removing the last admin email
|
||||
if (filteredEmails.length === 0) {
|
||||
return res.status(400).json({ error: 'Cannot remove all authorized emails' });
|
||||
}
|
||||
|
||||
// Note: This only updates the runtime environment variable
|
||||
process.env.AUTHORIZED_EMAILS = filteredEmails.join(',');
|
||||
|
||||
res.json({
|
||||
message: 'Email removed successfully',
|
||||
emails: filteredEmails,
|
||||
note: 'Changes are temporary. Update .env file for permanent changes.'
|
||||
});
|
||||
});
|
||||
|
||||
// Get system info (admin only)
|
||||
router.get('/system-info', authenticateToken, checkAdminAccess, (req, res) => {
|
||||
// Get system info
|
||||
router.get('/system-info', authenticateToken, (req, res) => {
|
||||
res.json({
|
||||
authorizedEmailsConfigured: !!process.env.AUTHORIZED_EMAILS,
|
||||
totalAuthorizedEmails: process.env.AUTHORIZED_EMAILS ? process.env.AUTHORIZED_EMAILS.split(',').length : 0,
|
||||
currentUser: req.user.email,
|
||||
isAdmin: true,
|
||||
environment: process.env.NODE_ENV || 'development'
|
||||
});
|
||||
});
|
||||
|
||||
// ==================== KREDITOR ROUTES ====================
|
||||
|
||||
// Get all kreditoren
|
||||
router.get('/kreditoren', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const result = await executeQuery('SELECT * FROM fibdash.Kreditor ORDER BY name');
|
||||
res.json({ kreditoren: result.recordset });
|
||||
} catch (error) {
|
||||
console.error('Error fetching kreditoren:', error);
|
||||
res.status(500).json({ error: 'Fehler beim Laden der Kreditoren' });
|
||||
}
|
||||
});
|
||||
|
||||
// Create new kreditor
|
||||
router.post('/kreditoren', authenticateToken, async (req, res) => {
|
||||
const { iban, name, kreditorId } = req.body;
|
||||
|
||||
if (!iban || !name || !kreditorId) {
|
||||
return res.status(400).json({ error: 'IBAN, Name und Kreditor ID sind erforderlich' });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeQuery(
|
||||
'INSERT INTO fibdash.Kreditor (iban, name, kreditorId) VALUES (@iban, @name, @kreditorId)',
|
||||
{ iban, name, kreditorId }
|
||||
);
|
||||
res.json({ message: 'Kreditor erfolgreich erstellt' });
|
||||
} catch (error) {
|
||||
console.error('Error creating kreditor:', error);
|
||||
if (error.number === 2627) { // Unique constraint violation
|
||||
res.status(400).json({ error: 'Kreditor ID bereits vorhanden' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Erstellen des Kreditors' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update kreditor
|
||||
router.put('/kreditoren/:id', authenticateToken, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { iban, name, kreditorId } = req.body;
|
||||
|
||||
if (!iban || !name || !kreditorId) {
|
||||
return res.status(400).json({ error: 'IBAN, Name und Kreditor ID sind erforderlich' });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeQuery(
|
||||
'UPDATE fibdash.Kreditor SET iban = @iban, name = @name, kreditorId = @kreditorId WHERE id = @id',
|
||||
{ iban, name, kreditorId, id }
|
||||
);
|
||||
res.json({ message: 'Kreditor erfolgreich aktualisiert' });
|
||||
} catch (error) {
|
||||
console.error('Error updating kreditor:', error);
|
||||
if (error.number === 2627) { // Unique constraint violation
|
||||
res.status(400).json({ error: 'Kreditor ID bereits vorhanden' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Aktualisieren des Kreditors' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delete kreditor
|
||||
router.delete('/kreditoren/:id', authenticateToken, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
await executeQuery('DELETE FROM fibdash.Kreditor WHERE id = @id', { id });
|
||||
res.json({ message: 'Kreditor erfolgreich gelöscht' });
|
||||
} catch (error) {
|
||||
console.error('Error deleting kreditor:', error);
|
||||
if (error.number === 547) { // Foreign key constraint violation
|
||||
res.status(400).json({ error: 'Kreditor kann nicht gelöscht werden, da er in Buchungen verwendet wird' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Löschen des Kreditors' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== KONTO ROUTES ====================
|
||||
|
||||
// Get all konten
|
||||
router.get('/konten', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const result = await executeQuery('SELECT * FROM fibdash.Konto ORDER BY konto');
|
||||
res.json({ konten: result.recordset });
|
||||
} catch (error) {
|
||||
console.error('Error fetching konten:', error);
|
||||
res.status(500).json({ error: 'Fehler beim Laden der Konten' });
|
||||
}
|
||||
});
|
||||
|
||||
// Create new konto
|
||||
router.post('/konten', authenticateToken, async (req, res) => {
|
||||
const { konto, name } = req.body;
|
||||
|
||||
if (!konto || !name) {
|
||||
return res.status(400).json({ error: 'Konto und Name sind erforderlich' });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeQuery(
|
||||
'INSERT INTO fibdash.Konto (konto, name) VALUES (@konto, @name)',
|
||||
{ konto, name }
|
||||
);
|
||||
res.json({ message: 'Konto erfolgreich erstellt' });
|
||||
} catch (error) {
|
||||
console.error('Error creating konto:', error);
|
||||
if (error.number === 2627) { // Unique constraint violation
|
||||
res.status(400).json({ error: 'Konto bereits vorhanden' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Erstellen des Kontos' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update konto
|
||||
router.put('/konten/:id', authenticateToken, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { konto, name } = req.body;
|
||||
|
||||
if (!konto || !name) {
|
||||
return res.status(400).json({ error: 'Konto und Name sind erforderlich' });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeQuery(
|
||||
'UPDATE fibdash.Konto SET konto = @konto, name = @name WHERE id = @id',
|
||||
{ konto, name, id }
|
||||
);
|
||||
res.json({ message: 'Konto erfolgreich aktualisiert' });
|
||||
} catch (error) {
|
||||
console.error('Error updating konto:', error);
|
||||
if (error.number === 2627) { // Unique constraint violation
|
||||
res.status(400).json({ error: 'Konto bereits vorhanden' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Aktualisieren des Kontos' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delete konto
|
||||
router.delete('/konten/:id', authenticateToken, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
await executeQuery('DELETE FROM fibdash.Konto WHERE id = @id', { id });
|
||||
res.json({ message: 'Konto erfolgreich gelöscht' });
|
||||
} catch (error) {
|
||||
console.error('Error deleting konto:', error);
|
||||
if (error.number === 547) { // Foreign key constraint violation
|
||||
res.status(400).json({ error: 'Konto kann nicht gelöscht werden, da es in Buchungen verwendet wird' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Löschen des Kontos' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== BU (BUCHUNGSSCHLÜSSEL) ROUTES ====================
|
||||
|
||||
// Get all buchungsschluessel
|
||||
router.get('/buchungsschluessel', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const result = await executeQuery('SELECT * FROM fibdash.BU ORDER BY bu');
|
||||
res.json({ buchungsschluessel: result.recordset });
|
||||
} catch (error) {
|
||||
console.error('Error fetching buchungsschluessel:', error);
|
||||
res.status(500).json({ error: 'Fehler beim Laden der Buchungsschlüssel' });
|
||||
}
|
||||
});
|
||||
|
||||
// Create new buchungsschluessel
|
||||
router.post('/buchungsschluessel', authenticateToken, async (req, res) => {
|
||||
const { bu, name, vst } = req.body;
|
||||
|
||||
if (!bu || !name) {
|
||||
return res.status(400).json({ error: 'BU und Name sind erforderlich' });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeQuery(
|
||||
'INSERT INTO fibdash.BU (bu, name, vst) VALUES (@bu, @name, @vst)',
|
||||
{ bu, name, vst: vst || null }
|
||||
);
|
||||
res.json({ message: 'Buchungsschlüssel erfolgreich erstellt' });
|
||||
} catch (error) {
|
||||
console.error('Error creating BU:', error);
|
||||
if (error.number === 2627) { // Unique constraint violation
|
||||
res.status(400).json({ error: 'Buchungsschlüssel bereits vorhanden' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Erstellen des Buchungsschlüssels' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update buchungsschluessel
|
||||
router.put('/buchungsschluessel/:id', authenticateToken, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { bu, name, vst } = req.body;
|
||||
|
||||
if (!bu || !name) {
|
||||
return res.status(400).json({ error: 'BU und Name sind erforderlich' });
|
||||
}
|
||||
|
||||
try {
|
||||
await executeQuery(
|
||||
'UPDATE fibdash.BU SET bu = @bu, name = @name, vst = @vst WHERE id = @id',
|
||||
{ bu, name, vst: vst || null, id }
|
||||
);
|
||||
res.json({ message: 'Buchungsschlüssel erfolgreich aktualisiert' });
|
||||
} catch (error) {
|
||||
console.error('Error updating BU:', error);
|
||||
if (error.number === 2627) { // Unique constraint violation
|
||||
res.status(400).json({ error: 'Buchungsschlüssel bereits vorhanden' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Aktualisieren des Buchungsschlüssels' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delete buchungsschluessel
|
||||
router.delete('/buchungsschluessel/:id', authenticateToken, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
await executeQuery('DELETE FROM fibdash.BU WHERE id = @id', { id });
|
||||
res.json({ message: 'Buchungsschlüssel erfolgreich gelöscht' });
|
||||
} catch (error) {
|
||||
console.error('Error deleting BU:', error);
|
||||
if (error.number === 547) { // Foreign key constraint violation
|
||||
res.status(400).json({ error: 'Buchungsschlüssel kann nicht gelöscht werden, da er in Buchungen verwendet wird' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'Fehler beim Löschen des Buchungsschlüssels' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user