231 Zeilen
6.8 KiB
JavaScript
231 Zeilen
6.8 KiB
JavaScript
/**
|
|
* 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;
|