diff --git a/server.js b/server.js index 4235ceb9..f4b3cef2 100644 --- a/server.js +++ b/server.js @@ -8,6 +8,7 @@ const hljs = require('highlight.js'); const WebSocket = require('ws'); const ClaudeCodeService = require('./services/claude-service'); const terminalService = require('./services/terminal-service'); +const { db } = require('./services/database'); const app = express(); const md = new MarkdownIt({ @@ -758,6 +759,172 @@ Created via Claude Code Web IDE } }); +// ============================================ +// Project CRUD API Endpoints (SQLite) +// ============================================ + +// GET /api/projects - List all active projects +app.get('/api/projects', requireAuth, (req, res) => { + try { + const projects = db.prepare(` + SELECT id, name, description, icon, color, path, createdAt, lastActivity + FROM projects + WHERE deletedAt IS NULL + ORDER BY lastActivity DESC + `).all(); + + // Add sessionCount (0 for now, will be implemented in Task 3) + const projectsWithSessionCount = projects.map(project => ({ + ...project, + sessionCount: 0 + })); + + res.json({ + success: true, + projects: projectsWithSessionCount + }); + } catch (error) { + console.error('Error listing projects:', error); + res.status(500).json({ error: 'Failed to list projects' }); + } +}); + +// POST /api/projects - Create new project +app.post('/api/projects', requireAuth, (req, res) => { + try { + const { name, path: projectPath, description, icon, color } = req.body; + + // Validate required fields + if (!name || !projectPath) { + return res.status(400).json({ error: 'Name and path are required' }); + } + + // Check for duplicate names (only among non-deleted projects) + const existing = db.prepare(` + SELECT id FROM projects + WHERE name = ? AND deletedAt IS NULL + `).get(name); + + if (existing) { + return res.status(409).json({ error: 'Project with this name already exists' }); + } + + const now = new Date().toISOString(); + + // Set defaults + const projectIcon = icon || '📁'; + const projectColor = color || '#4a9eff'; + + const result = db.prepare(` + INSERT INTO projects (name, description, icon, color, path, createdAt, lastActivity) + VALUES (?, ?, ?, ?, ?, ?, ?) + `).run(name, description || null, projectIcon, projectColor, projectPath, now, now); + + // Get the inserted project + const project = db.prepare(` + SELECT id, name, description, icon, color, path, createdAt, lastActivity + FROM projects + WHERE id = ? + `).get(result.lastInsertRowid); + + res.status(201).json({ + success: true, + project: { + ...project, + sessionCount: 0 + } + }); + } catch (error) { + console.error('Error creating project:', error); + res.status(500).json({ error: 'Failed to create project' }); + } +}); + +// PUT /api/projects/:id - Update project +app.put('/api/projects/:id', requireAuth, (req, res) => { + try { + const { id } = req.params; + const { name, description, icon, color, path: projectPath } = req.body; + + // Check if project exists and is not deleted + const existing = db.prepare(` + SELECT id FROM projects + WHERE id = ? AND deletedAt IS NULL + `).get(id); + + if (!existing) { + return res.status(404).json({ error: 'Project not found' }); + } + + // If updating name, check for duplicates + if (name) { + const duplicate = db.prepare(` + SELECT id FROM projects + WHERE name = ? AND id != ? AND deletedAt IS NULL + `).get(name, id); + + if (duplicate) { + return res.status(409).json({ error: 'Project with this name already exists' }); + } + } + + // Build update query dynamically based on provided fields + const updates = []; + const values = []; + + if (name !== undefined) { + updates.push('name = ?'); + values.push(name); + } + if (description !== undefined) { + updates.push('description = ?'); + values.push(description); + } + if (icon !== undefined) { + updates.push('icon = ?'); + values.push(icon); + } + if (color !== undefined) { + updates.push('color = ?'); + values.push(color); + } + if (projectPath !== undefined) { + updates.push('path = ?'); + values.push(projectPath); + } + + if (updates.length === 0) { + return res.status(400).json({ error: 'No fields to update' }); + } + + values.push(id); + + db.prepare(` + UPDATE projects + SET ${updates.join(', ')} + WHERE id = ? AND deletedAt IS NULL + `).run(...values); + + // Get the updated project + const project = db.prepare(` + SELECT id, name, description, icon, color, path, createdAt, lastActivity + FROM projects + WHERE id = ? + `).get(id); + + res.json({ + success: true, + project: { + ...project, + sessionCount: 0 + } + }); + } catch (error) { + console.error('Error updating project:', error); + res.status(500).json({ error: 'Failed to update project' }); + } +}); + // ===== Terminal API Endpoints ===== // Create a new terminal