UNPKG

cueit

Version:

Cueit - A Kanban board orchestrated by LLMs via MCP

200 lines (164 loc) 6.69 kB
import { getDatabase } from '../utils/database.js'; import { SQL_QUERIES } from '../utils/sqlQueries.js'; // GET /api/history/:projectId - Get version history for a project export const getProjectHistory = async (req, res) => { try { const { projectId } = req.params; const limit = parseInt(req.query.limit) || 20; if (!projectId) { return res.status(400).json({ error: 'Missing project ID' }); } const db = getDatabase(); // Verify project exists const project = db.prepare(SQL_QUERIES.VERIFY_PROJECT_EXISTS).get(projectId); if (!project) { return res.status(404).json({ error: 'Project not found' }); } const history = db.prepare(SQL_QUERIES.SELECT_PROJECT_HISTORY).all(projectId, limit); res.json({ history }); } catch (error) { console.error('getProjectHistory error:', error); res.status(500).json({ error: 'Failed to fetch project history' }); } }; // POST /api/history/:projectId/restore/:versionId - Restore project to a specific version export const restoreProjectVersion = async (req, res) => { try { const { projectId, versionId } = req.params; if (!projectId || !versionId) { return res.status(400).json({ error: 'Missing project ID or version ID' }); } const db = getDatabase(); // Get the version snapshot const version = db.prepare(SQL_QUERIES.SELECT_PROJECT_HISTORY_BY_ID).get(versionId); if (!version || version.project_id != projectId) { return res.status(404).json({ error: 'Version not found' }); } // Parse the snapshot let snapshotData; try { snapshotData = JSON.parse(version.snapshot); } catch (error) { return res.status(500).json({ error: 'Invalid snapshot data' }); } // Create a new version in the history before restoring (backup current data) await saveProjectVersion(projectId, `Backup before restore to version ${versionId}`); // Start transaction to restore the project const transaction = db.transaction(() => { // Delete existing data (complete replacement approach) db.prepare('DELETE FROM subtasks WHERE task_id IN (SELECT id FROM tasks WHERE project_id = ?)').run(projectId); db.prepare('DELETE FROM tasks WHERE project_id = ?').run(projectId); db.prepare('DELETE FROM columns WHERE project_id = ?').run(projectId); // Update project if (snapshotData.project) { db.prepare(SQL_QUERIES.UPDATE_PROJECT).run( snapshotData.project.name, snapshotData.project.description, projectId ); } // Restore columns if (snapshotData.columns && snapshotData.columns.length > 0) { const columnStmt = db.prepare(SQL_QUERIES.INSERT_COLUMN); const columnIdMap = {}; snapshotData.columns.forEach(column => { const result = columnStmt.run(projectId, column.name, column.order_index); columnIdMap[column.id] = result.lastInsertRowid; }); // Restore tasks if (snapshotData.tasks && snapshotData.tasks.length > 0) { const taskStmt = db.prepare(SQL_QUERIES.INSERT_TASK); const taskIdMap = {}; snapshotData.tasks.forEach(task => { const newColumnId = columnIdMap[task.column_id]; if (newColumnId) { const result = taskStmt.run( projectId, newColumnId, task.title, task.description, task.display_id, task.order_index ); taskIdMap[task.id] = result.lastInsertRowid; } }); // Restore subtasks if (snapshotData.subtasks && snapshotData.subtasks.length > 0) { const subtaskStmt = db.prepare(SQL_QUERIES.INSERT_SUBTASK); snapshotData.subtasks.forEach(subtask => { const newTaskId = taskIdMap[subtask.task_id]; if (newTaskId) { subtaskStmt.run( newTaskId, subtask.title, subtask.completed, subtask.order_index ); } }); } } } }); transaction(); res.json({ success: true, message: 'Project restored successfully' }); } catch (error) { console.error('restoreProjectVersion error:', error); res.status(500).json({ error: 'Failed to restore project version' }); } }; // POST /api/history/:projectId/save - Manually save current project state export const saveCurrentProjectVersion = async (req, res) => { try { const { projectId } = req.params; const { description } = req.body; if (!projectId) { return res.status(400).json({ error: 'Missing project ID' }); } if (!description || !description.trim()) { return res.status(400).json({ error: 'Description is required' }); } const success = await saveProjectVersion(projectId, description.trim()); if (success) { res.json({ success: true, message: 'Project version saved successfully' }); } else { res.status(500).json({ error: 'Failed to save project version' }); } } catch (error) { console.error('saveCurrentProjectVersion error:', error); res.status(500).json({ error: 'Failed to save project version' }); } }; // Helper function to save project version export const saveProjectVersion = async (projectId, description) => { try { const db = getDatabase(); // Get complete project data using existing queries const projectData = db.prepare(SQL_QUERIES.SELECT_PROJECT_BY_ID).get(projectId); if (!projectData) { throw new Error('Project not found'); } const columns = db.prepare(SQL_QUERIES.SELECT_COLUMNS_BY_PROJECT).all(projectId); const tasks = db.prepare(SQL_QUERIES.SELECT_TASKS_BY_PROJECT).all(projectId); const subtasks = db.prepare(SQL_QUERIES.SELECT_SUBTASKS_BY_PROJECT).all(projectId); // Create snapshot const snapshot = { project: projectData, columns: columns, tasks: tasks, subtasks: subtasks, timestamp: new Date().toISOString() }; // Save version const insertStmt = db.prepare(SQL_QUERIES.INSERT_PROJECT_HISTORY); insertStmt.run(projectId, JSON.stringify(snapshot), description); // Clean up old versions (keep only latest 50) const cleanupStmt = db.prepare(SQL_QUERIES.DELETE_OLD_PROJECT_HISTORY); cleanupStmt.run(projectId, projectId, 50); return true; } catch (error) { console.error('saveProjectVersion error:', error); return false; } };