File Actions Implementation Instructions
Overview
This document provides instructions for implementing the backend download endpoint to support the new file actions column in the FileTree component.
Frontend Changes Completed
- Changed "Permissions" column header to "Actions"
- Added Phosphor icons: Download, Copy, Lock, Edit2
- Replaced permissions text with action buttons:
- Lock/Edit2 icon for read-only vs writable files
- Download button for files
- Copy path button for all items
- Added
handleDownloadandhandleCopyPathfunctions - Updated
api.jswithdownloadFilemethod
Backend Implementation Required
Download Endpoint
Create a new endpoint in the server to handle file downloads:
File: server/routes/files.js (if doesn't exist, create it)
import express from 'express';
import path from 'path';
import fs from 'fs';
import { authenticateToken } from '../middleware/auth.js';
const router = express.Router();
// Download file endpoint
router.get('/projects/:projectName/files/download', authenticateToken, async (req, res) => {
try {
const { projectName } = req.params;
const { path: filePath } = req.query;
if (!filePath) {
return res.status(400).json({ error: 'File path is required' });
}
// Construct the full file path
const projectsDir = process.env.PROJECTS_DIR || path.join(process.cwd(), 'projects');
const fullPath = path.join(projectsDir, projectName, filePath);
// Security: Ensure the path doesn't escape the project directory
const resolvedPath = path.resolve(fullPath);
const projectRoot = path.resolve(path.join(projectsDir, projectName));
if (!resolvedPath.startsWith(projectRoot)) {
return res.status(403).json({ error: 'Access denied' });
}
// Check if file exists
if (!fs.existsSync(resolvedPath)) {
return res.status(404).json({ error: 'File not found' });
}
// Check if it's a file (not a directory)
const stats = fs.statSync(resolvedPath);
if (!stats.isFile()) {
return res.status(400).json({ error: 'Path is not a file' });
}
// Get the filename for the download
const filename = path.basename(filePath);
// Set appropriate headers
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.setHeader('Content-Type', 'application/octet-stream');
// Stream the file to the response
const fileStream = fs.createReadStream(resolvedPath);
fileStream.pipe(res);
fileStream.on('error', (error) => {
console.error('Error streaming file:', error);
if (!res.headersSent) {
res.status(500).json({ error: 'Error downloading file' });
}
});
} catch (error) {
console.error('Download error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;
Update server/index.js
Add the files router to the main server file:
// Add to imports at the top
import filesRoutes from './routes/files.js';
// Add after other route definitions (around line 280-290)
app.use('/api', filesRoutes);
Testing the Implementation
Test Download Button:
- Click download icon on various file types
- Verify file downloads with correct name
- Check that directories don't show download button
Test Copy Path Button:
- Click copy icon and paste to verify path
- Ensure full project path is included
Test Read/Write Indicators:
- Verify Lock icon shows for read-only files
- Verify Edit2 icon shows for writable files
- Check that indicators only show for files, not directories
Security Testing:
- Try downloading files with path traversal attempts (../)
- Verify only files within project directory are accessible
Future Enhancements
- Toast Notifications: Add feedback when copying path or download completes
- Batch Downloads: Allow selecting multiple files for download
- Preview Button: Quick preview without opening full editor
- Upload Progress: Show progress for large file uploads
- File Actions Context Menu: Right-click menu with more options
Notes
- The download endpoint uses streaming to handle large files efficiently
- Path security is critical - always validate paths don't escape project directory
- Consider adding rate limiting for download endpoint to prevent abuse
- The frontend already handles errors gracefully with console logging