/** * TASKMATE - Export Routes * ======================== * Export in JSON und CSV */ const express = require('express'); const router = express.Router(); const { getDb } = require('../database'); const logger = require('../utils/logger'); /** * GET /api/export/project/:id/json * Projekt als JSON exportieren */ router.get('/project/:id/json', (req, res) => { try { const projectId = req.params.id; const db = getDb(); // Projekt const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(projectId); if (!project) { return res.status(404).json({ error: 'Projekt nicht gefunden' }); } // Spalten const columns = db.prepare('SELECT * FROM columns WHERE project_id = ? ORDER BY position').all(projectId); // Labels const labels = db.prepare('SELECT * FROM labels WHERE project_id = ?').all(projectId); // Aufgaben mit allen Details const tasks = db.prepare('SELECT * FROM tasks WHERE project_id = ?').all(projectId); const tasksWithDetails = tasks.map(task => { const taskLabels = db.prepare(` SELECT l.* FROM labels l JOIN task_labels tl ON l.id = tl.label_id WHERE tl.task_id = ? `).all(task.id); const subtasks = db.prepare('SELECT * FROM subtasks WHERE task_id = ? ORDER BY position').all(task.id); const comments = db.prepare(` SELECT c.*, u.display_name FROM comments c LEFT JOIN users u ON c.user_id = u.id WHERE c.task_id = ? `).all(task.id); const attachments = db.prepare('SELECT * FROM attachments WHERE task_id = ?').all(task.id); const links = db.prepare('SELECT * FROM links WHERE task_id = ?').all(task.id); return { ...task, labels: taskLabels, subtasks, comments, attachments, links }; }); // Vorlagen const templates = db.prepare('SELECT * FROM task_templates WHERE project_id = ?').all(projectId); const exportData = { exportedAt: new Date().toISOString(), exportedBy: req.user.username, version: '1.0', project: { id: project.id, name: project.name, description: project.description, createdAt: project.created_at }, columns: columns.map(c => ({ id: c.id, name: c.name, position: c.position, color: c.color })), labels: labels.map(l => ({ id: l.id, name: l.name, color: l.color })), tasks: tasksWithDetails, templates }; logger.info(`Projekt exportiert als JSON: ${project.name}`); res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Disposition', `attachment; filename="${project.name.replace(/[^a-z0-9]/gi, '_')}_export.json"`); res.json(exportData); } catch (error) { logger.error('Fehler beim JSON-Export:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * GET /api/export/project/:id/csv * Aufgaben als CSV exportieren */ router.get('/project/:id/csv', (req, res) => { try { const projectId = req.params.id; const db = getDb(); const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(projectId); if (!project) { return res.status(404).json({ error: 'Projekt nicht gefunden' }); } const tasks = db.prepare(` SELECT t.*, c.name as column_name, u.display_name as assigned_name FROM tasks t LEFT JOIN columns c ON t.column_id = c.id LEFT JOIN users u ON t.assigned_to = u.id WHERE t.project_id = ? ORDER BY c.position, t.position `).all(projectId); // CSV Header const headers = [ 'ID', 'Titel', 'Beschreibung', 'Status', 'Priorität', 'Fälligkeitsdatum', 'Zugewiesen an', 'Zeitschätzung (Min)', 'Erstellt am', 'Archiviert' ]; // CSV Zeilen const rows = tasks.map(task => [ task.id, escapeCsvField(task.title), escapeCsvField(task.description || ''), task.column_name, task.priority, task.due_date || '', task.assigned_name || '', task.time_estimate_min || '', task.created_at, task.archived ? 'Ja' : 'Nein' ]); // CSV zusammenbauen const csv = [ headers.join(';'), ...rows.map(row => row.join(';')) ].join('\n'); logger.info(`Projekt exportiert als CSV: ${project.name}`); res.setHeader('Content-Type', 'text/csv; charset=utf-8'); res.setHeader('Content-Disposition', `attachment; filename="${project.name.replace(/[^a-z0-9]/gi, '_')}_export.csv"`); // BOM für Excel UTF-8 Erkennung res.send('\ufeff' + csv); } catch (error) { logger.error('Fehler beim CSV-Export:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * GET /api/export/all/json * Alle Daten exportieren (Backup) */ router.get('/all/json', (req, res) => { try { const db = getDb(); const projects = db.prepare('SELECT * FROM projects').all(); const columns = db.prepare('SELECT * FROM columns').all(); const labels = db.prepare('SELECT * FROM labels').all(); const tasks = db.prepare('SELECT * FROM tasks').all(); const subtasks = db.prepare('SELECT * FROM subtasks').all(); const comments = db.prepare('SELECT * FROM comments').all(); const taskLabels = db.prepare('SELECT * FROM task_labels').all(); const attachments = db.prepare('SELECT * FROM attachments').all(); const links = db.prepare('SELECT * FROM links').all(); const templates = db.prepare('SELECT * FROM task_templates').all(); const history = db.prepare('SELECT * FROM history').all(); const exportData = { exportedAt: new Date().toISOString(), exportedBy: req.user.username, version: '1.0', data: { projects, columns, labels, tasks, subtasks, comments, taskLabels, attachments, links, templates, history } }; logger.info('Vollständiger Export durchgeführt'); res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Disposition', `attachment; filename="taskmate_backup_${Date.now()}.json"`); res.json(exportData); } catch (error) { logger.error('Fehler beim Voll-Export:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * Hilfsfunktion: CSV-Feld escapen */ function escapeCsvField(field) { if (typeof field !== 'string') return field; // Wenn Feld Semikolon, Anführungszeichen oder Zeilenumbruch enthält if (field.includes(';') || field.includes('"') || field.includes('\n')) { // Anführungszeichen verdoppeln und in Anführungszeichen setzen return '"' + field.replace(/"/g, '""') + '"'; } return field; } module.exports = router;