feat: add soft delete, restore, permanent delete, and recycle bin endpoints
- Add DELETE /api/projects/:id - Soft delete project (sets deletedAt) - Add POST /api/projects/:id/restore - Restore from recycle bin - Add DELETE /api/projects/:id/permanent - Permanent delete - Add GET /api/recycle-bin - List deleted items sorted by deletedAt DESC Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
131
server.js
131
server.js
@@ -977,6 +977,137 @@ app.put('/api/projects/:id', requireAuth, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/projects/:id - Soft delete project
|
||||
app.delete('/api/projects/:id', requireAuth, (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Validate ID
|
||||
const validatedId = validateProjectId(id);
|
||||
if (!validatedId) {
|
||||
return res.status(400).json({ error: 'Invalid project ID' });
|
||||
}
|
||||
|
||||
// Check if project exists and is not already deleted
|
||||
const existing = db.prepare(`
|
||||
SELECT id FROM projects
|
||||
WHERE id = ? AND deletedAt IS NULL
|
||||
`).get(validatedId);
|
||||
|
||||
if (!existing) {
|
||||
return res.status(404).json({ error: 'Project not found' });
|
||||
}
|
||||
|
||||
// Soft delete by setting deletedAt
|
||||
const now = new Date().toISOString();
|
||||
db.prepare(`
|
||||
UPDATE projects
|
||||
SET deletedAt = ?
|
||||
WHERE id = ?
|
||||
`).run(now, validatedId);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error soft deleting project:', error);
|
||||
res.status(500).json({ error: 'Failed to delete project' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/projects/:id/restore - Restore project from recycle bin
|
||||
app.post('/api/projects/:id/restore', requireAuth, (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Validate ID
|
||||
const validatedId = validateProjectId(id);
|
||||
if (!validatedId) {
|
||||
return res.status(400).json({ error: 'Invalid project ID' });
|
||||
}
|
||||
|
||||
// Check if project exists and is in recycle bin
|
||||
const existing = db.prepare(`
|
||||
SELECT id FROM projects
|
||||
WHERE id = ? AND deletedAt IS NOT NULL
|
||||
`).get(validatedId);
|
||||
|
||||
if (!existing) {
|
||||
return res.status(404).json({ error: 'Project not found in recycle bin' });
|
||||
}
|
||||
|
||||
// Restore by setting deletedAt to NULL
|
||||
db.prepare(`
|
||||
UPDATE projects
|
||||
SET deletedAt = NULL
|
||||
WHERE id = ?
|
||||
`).run(validatedId);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error restoring project:', error);
|
||||
res.status(500).json({ error: 'Failed to restore project' });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/projects/:id/permanent - Permanently delete project
|
||||
app.delete('/api/projects/:id/permanent', requireAuth, (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Validate ID
|
||||
const validatedId = validateProjectId(id);
|
||||
if (!validatedId) {
|
||||
return res.status(400).json({ error: 'Invalid project ID' });
|
||||
}
|
||||
|
||||
// Check if project exists
|
||||
const existing = db.prepare(`
|
||||
SELECT id FROM projects
|
||||
WHERE id = ?
|
||||
`).get(validatedId);
|
||||
|
||||
if (!existing) {
|
||||
return res.status(404).json({ error: 'Project not found' });
|
||||
}
|
||||
|
||||
// Permanently delete the project
|
||||
db.prepare(`
|
||||
DELETE FROM projects
|
||||
WHERE id = ?
|
||||
`).run(validatedId);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error permanently deleting project:', error);
|
||||
res.status(500).json({ error: 'Failed to permanently delete project' });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/recycle-bin - List deleted items
|
||||
app.get('/api/recycle-bin', requireAuth, (req, res) => {
|
||||
try {
|
||||
const items = db.prepare(`
|
||||
SELECT id, name, description, icon, path, deletedAt
|
||||
FROM projects
|
||||
WHERE deletedAt IS NOT NULL
|
||||
ORDER BY deletedAt DESC
|
||||
`).all();
|
||||
|
||||
// Add sessionCount (0 for now, will be implemented in Task 4)
|
||||
const itemsWithSessionCount = items.map(item => ({
|
||||
...item,
|
||||
sessionCount: 0
|
||||
}));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
items: itemsWithSessionCount
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error listing recycle bin:', error);
|
||||
res.status(500).json({ error: 'Failed to list recycle bin' });
|
||||
}
|
||||
});
|
||||
|
||||
// ===== Terminal API Endpoints =====
|
||||
|
||||
// Create a new terminal
|
||||
|
||||
Reference in New Issue
Block a user