270 Zeilen
7.3 KiB
JavaScript
270 Zeilen
7.3 KiB
JavaScript
/**
|
|
* TASKMATE - Import Routes
|
|
* ========================
|
|
* Import von JSON-Backups
|
|
*/
|
|
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const { getDb } = require('../database');
|
|
const logger = require('../utils/logger');
|
|
|
|
/**
|
|
* POST /api/import/project
|
|
* Projekt aus JSON importieren
|
|
*/
|
|
router.post('/project', (req, res) => {
|
|
try {
|
|
const { data, overwrite = false } = req.body;
|
|
|
|
if (!data || !data.project) {
|
|
return res.status(400).json({ error: 'Ungültiges Import-Format' });
|
|
}
|
|
|
|
const db = getDb();
|
|
|
|
// Transaktion starten
|
|
const importProject = db.transaction(() => {
|
|
const importData = data;
|
|
|
|
// Projekt erstellen
|
|
const projectResult = db.prepare(`
|
|
INSERT INTO projects (name, description, created_by)
|
|
VALUES (?, ?, ?)
|
|
`).run(
|
|
importData.project.name + (overwrite ? '' : ' (Import)'),
|
|
importData.project.description,
|
|
req.user.id
|
|
);
|
|
|
|
const newProjectId = projectResult.lastInsertRowid;
|
|
|
|
// Mapping für alte -> neue IDs
|
|
const columnMap = new Map();
|
|
const labelMap = new Map();
|
|
const taskMap = new Map();
|
|
|
|
// Spalten importieren
|
|
if (importData.columns) {
|
|
const insertColumn = db.prepare(`
|
|
INSERT INTO columns (project_id, name, position, color)
|
|
VALUES (?, ?, ?, ?)
|
|
`);
|
|
|
|
importData.columns.forEach(col => {
|
|
const result = insertColumn.run(newProjectId, col.name, col.position, col.color);
|
|
columnMap.set(col.id, result.lastInsertRowid);
|
|
});
|
|
}
|
|
|
|
// Labels importieren
|
|
if (importData.labels) {
|
|
const insertLabel = db.prepare(`
|
|
INSERT INTO labels (project_id, name, color)
|
|
VALUES (?, ?, ?)
|
|
`);
|
|
|
|
importData.labels.forEach(label => {
|
|
const result = insertLabel.run(newProjectId, label.name, label.color);
|
|
labelMap.set(label.id, result.lastInsertRowid);
|
|
});
|
|
}
|
|
|
|
// Aufgaben importieren
|
|
if (importData.tasks) {
|
|
const insertTask = db.prepare(`
|
|
INSERT INTO tasks (
|
|
project_id, column_id, title, description, priority,
|
|
due_date, time_estimate_min, position, created_by
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`);
|
|
|
|
importData.tasks.forEach(task => {
|
|
const newColumnId = columnMap.get(task.column_id);
|
|
if (!newColumnId) return;
|
|
|
|
const result = insertTask.run(
|
|
newProjectId,
|
|
newColumnId,
|
|
task.title,
|
|
task.description,
|
|
task.priority || 'medium',
|
|
task.due_date,
|
|
task.time_estimate_min,
|
|
task.position,
|
|
req.user.id
|
|
);
|
|
|
|
taskMap.set(task.id, result.lastInsertRowid);
|
|
|
|
// Task-Labels
|
|
if (task.labels) {
|
|
const insertTaskLabel = db.prepare(
|
|
'INSERT INTO task_labels (task_id, label_id) VALUES (?, ?)'
|
|
);
|
|
task.labels.forEach(label => {
|
|
const newLabelId = labelMap.get(label.id);
|
|
if (newLabelId) {
|
|
try {
|
|
insertTaskLabel.run(result.lastInsertRowid, newLabelId);
|
|
} catch (e) { /* Ignorieren */ }
|
|
}
|
|
});
|
|
}
|
|
|
|
// Subtasks
|
|
if (task.subtasks) {
|
|
const insertSubtask = db.prepare(
|
|
'INSERT INTO subtasks (task_id, title, completed, position) VALUES (?, ?, ?, ?)'
|
|
);
|
|
task.subtasks.forEach(st => {
|
|
insertSubtask.run(
|
|
result.lastInsertRowid,
|
|
st.title,
|
|
st.completed ? 1 : 0,
|
|
st.position
|
|
);
|
|
});
|
|
}
|
|
|
|
// Links
|
|
if (task.links) {
|
|
const insertLink = db.prepare(
|
|
'INSERT INTO links (task_id, title, url, created_by) VALUES (?, ?, ?, ?)'
|
|
);
|
|
task.links.forEach(link => {
|
|
insertLink.run(result.lastInsertRowid, link.title, link.url, req.user.id);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Vorlagen importieren
|
|
if (importData.templates) {
|
|
const insertTemplate = db.prepare(`
|
|
INSERT INTO task_templates (
|
|
project_id, name, title_template, description,
|
|
priority, labels, subtasks, time_estimate_min
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
`);
|
|
|
|
importData.templates.forEach(tmpl => {
|
|
// Labels-IDs mappen
|
|
let newLabels = null;
|
|
if (tmpl.labels) {
|
|
const oldLabels = typeof tmpl.labels === 'string' ? JSON.parse(tmpl.labels) : tmpl.labels;
|
|
const mappedLabels = oldLabels.map(id => labelMap.get(id)).filter(id => id);
|
|
newLabels = JSON.stringify(mappedLabels);
|
|
}
|
|
|
|
insertTemplate.run(
|
|
newProjectId,
|
|
tmpl.name,
|
|
tmpl.title_template,
|
|
tmpl.description,
|
|
tmpl.priority,
|
|
newLabels,
|
|
tmpl.subtasks,
|
|
tmpl.time_estimate_min
|
|
);
|
|
});
|
|
}
|
|
|
|
return {
|
|
projectId: newProjectId,
|
|
columnsImported: columnMap.size,
|
|
labelsImported: labelMap.size,
|
|
tasksImported: taskMap.size
|
|
};
|
|
});
|
|
|
|
const result = importProject();
|
|
|
|
logger.info(`Projekt importiert: ID ${result.projectId} (${result.tasksImported} Aufgaben)`);
|
|
|
|
res.status(201).json({
|
|
message: 'Import erfolgreich',
|
|
...result
|
|
});
|
|
} catch (error) {
|
|
logger.error('Fehler beim Import:', { error: error.message });
|
|
res.status(500).json({ error: 'Import fehlgeschlagen: ' + error.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/import/validate
|
|
* Import-Datei validieren
|
|
*/
|
|
router.post('/validate', (req, res) => {
|
|
try {
|
|
const { data } = req.body;
|
|
|
|
const errors = [];
|
|
const warnings = [];
|
|
|
|
if (!data) {
|
|
errors.push('Keine Daten vorhanden');
|
|
return res.json({ valid: false, errors, warnings });
|
|
}
|
|
|
|
// Version prüfen
|
|
if (!data.version) {
|
|
warnings.push('Keine Versionsangabe gefunden');
|
|
}
|
|
|
|
// Projekt prüfen
|
|
if (!data.project) {
|
|
errors.push('Kein Projekt in den Daten gefunden');
|
|
} else {
|
|
if (!data.project.name) {
|
|
errors.push('Projektname fehlt');
|
|
}
|
|
}
|
|
|
|
// Spalten prüfen
|
|
if (!data.columns || data.columns.length === 0) {
|
|
errors.push('Keine Spalten in den Daten gefunden');
|
|
}
|
|
|
|
// Aufgaben prüfen
|
|
if (data.tasks) {
|
|
data.tasks.forEach((task, idx) => {
|
|
if (!task.title) {
|
|
warnings.push(`Aufgabe ${idx + 1} hat keinen Titel`);
|
|
}
|
|
if (!task.column_id) {
|
|
warnings.push(`Aufgabe "${task.title || idx + 1}" hat keine Spalten-ID`);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Statistiken
|
|
const stats = {
|
|
projectName: data.project?.name || 'Unbekannt',
|
|
columns: data.columns?.length || 0,
|
|
labels: data.labels?.length || 0,
|
|
tasks: data.tasks?.length || 0,
|
|
templates: data.templates?.length || 0
|
|
};
|
|
|
|
res.json({
|
|
valid: errors.length === 0,
|
|
errors,
|
|
warnings,
|
|
stats
|
|
});
|
|
} catch (error) {
|
|
logger.error('Fehler bei Import-Validierung:', { error: error.message });
|
|
res.status(400).json({
|
|
valid: false,
|
|
errors: ['Ungültiges JSON-Format'],
|
|
warnings: []
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|