203 Zeilen
5.6 KiB
JavaScript
203 Zeilen
5.6 KiB
JavaScript
/**
|
|
* 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;
|