/** * TASKMATE - Label Routes * ======================= * CRUD für Labels/Tags */ const express = require('express'); const router = express.Router(); const { getDb } = require('../database'); const logger = require('../utils/logger'); const { validators } = require('../middleware/validation'); /** * GET /api/labels/:projectId * Alle Labels eines Projekts */ router.get('/:projectId', (req, res) => { try { const db = getDb(); const labels = db.prepare(` SELECT l.*, (SELECT COUNT(*) FROM task_labels tl WHERE tl.label_id = l.id) as task_count FROM labels l WHERE l.project_id = ? ORDER BY l.name `).all(req.params.projectId); res.json(labels.map(l => ({ id: l.id, projectId: l.project_id, name: l.name, color: l.color, taskCount: l.task_count }))); } catch (error) { logger.error('Fehler beim Abrufen der Labels:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * POST /api/labels * Neues Label erstellen */ router.post('/', (req, res) => { try { const { projectId, name, color } = req.body; // Validierung const errors = []; errors.push(validators.required(projectId, 'Projekt-ID')); errors.push(validators.required(name, 'Name')); errors.push(validators.maxLength(name, 30, 'Name')); errors.push(validators.required(color, 'Farbe')); errors.push(validators.hexColor(color, 'Farbe')); const firstError = errors.find(e => e !== null); if (firstError) { return res.status(400).json({ error: firstError }); } const db = getDb(); // Prüfen ob Label-Name bereits existiert const existing = db.prepare( 'SELECT id FROM labels WHERE project_id = ? AND LOWER(name) = LOWER(?)' ).get(projectId, name); if (existing) { return res.status(400).json({ error: 'Ein Label mit diesem Namen existiert bereits' }); } const result = db.prepare(` INSERT INTO labels (project_id, name, color) VALUES (?, ?, ?) `).run(projectId, name, color); const label = db.prepare('SELECT * FROM labels WHERE id = ?').get(result.lastInsertRowid); logger.info(`Label erstellt: ${name} in Projekt ${projectId}`); // WebSocket const io = req.app.get('io'); io.to(`project:${projectId}`).emit('label:created', { id: label.id, projectId: label.project_id, name: label.name, color: label.color }); res.status(201).json({ id: label.id, projectId: label.project_id, name: label.name, color: label.color, taskCount: 0 }); } catch (error) { logger.error('Fehler beim Erstellen des Labels:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * PUT /api/labels/:id * Label aktualisieren */ router.put('/:id', (req, res) => { try { const labelId = req.params.id; const { name, color } = req.body; // Validierung if (name) { const nameError = validators.maxLength(name, 30, 'Name'); if (nameError) return res.status(400).json({ error: nameError }); } if (color) { const colorError = validators.hexColor(color, 'Farbe'); if (colorError) return res.status(400).json({ error: colorError }); } const db = getDb(); const existing = db.prepare('SELECT * FROM labels WHERE id = ?').get(labelId); if (!existing) { return res.status(404).json({ error: 'Label nicht gefunden' }); } // Prüfen ob neuer Name bereits existiert if (name && name.toLowerCase() !== existing.name.toLowerCase()) { const duplicate = db.prepare( 'SELECT id FROM labels WHERE project_id = ? AND LOWER(name) = LOWER(?) AND id != ?' ).get(existing.project_id, name, labelId); if (duplicate) { return res.status(400).json({ error: 'Ein Label mit diesem Namen existiert bereits' }); } } db.prepare(` UPDATE labels SET name = COALESCE(?, name), color = COALESCE(?, color) WHERE id = ? `).run(name || null, color || null, labelId); const label = db.prepare('SELECT * FROM labels WHERE id = ?').get(labelId); logger.info(`Label aktualisiert: ${label.name} (ID: ${labelId})`); // WebSocket const io = req.app.get('io'); io.to(`project:${label.project_id}`).emit('label:updated', { id: label.id, name: label.name, color: label.color }); res.json({ id: label.id, projectId: label.project_id, name: label.name, color: label.color }); } catch (error) { logger.error('Fehler beim Aktualisieren des Labels:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * DELETE /api/labels/:id * Label löschen */ router.delete('/:id', (req, res) => { try { const labelId = req.params.id; const db = getDb(); const label = db.prepare('SELECT * FROM labels WHERE id = ?').get(labelId); if (!label) { return res.status(404).json({ error: 'Label nicht gefunden' }); } // Label wird von task_labels durch CASCADE gelöscht db.prepare('DELETE FROM labels WHERE id = ?').run(labelId); logger.info(`Label gelöscht: ${label.name} (ID: ${labelId})`); // WebSocket const io = req.app.get('io'); io.to(`project:${label.project_id}`).emit('label:deleted', { id: labelId }); res.json({ message: 'Label gelöscht' }); } catch (error) { logger.error('Fehler beim Löschen des Labels:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); module.exports = router;