Skip to content

Commit 3fb9f05

Browse files
Copilotkobenguyent
andauthored
fix: file loading API and WebSocket connection issues (#571)
* Initial plan * Fix file API and WebSocket URL issues - Fix POST /api/file to read from request body (path parameter) - Add backward compatibility for existing file API usage - Fix WebSocket URL construction to use ws:/wss: protocol - Add JSON body parser to file endpoint - Maintain streaming for legacy requests while adding JSON response for frontend Co-authored-by: kobenguyent <[email protected]> * Clean up temporary test files Co-authored-by: kobenguyent <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: kobenguyent <[email protected]>
1 parent f28345c commit 3fb9f05

File tree

3 files changed

+43
-25
lines changed

3 files changed

+43
-25
lines changed

lib/api/get-file.js

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ const path = require('path');
55
const MAX_FILE_SIZE = 10 * 1024 * 1024;
66

77
module.exports = async (req, res) => {
8-
if (!req.file) {
8+
// Support both old format (req.file) and new format (req.body.path)
9+
const filePath = (req.body && req.body.path) || req.file;
10+
11+
if (!filePath) {
912
return res.status(400).json({ error: 'File parameter is required' });
1013
}
1114

12-
if (!req.file.startsWith(global.codecept_dir)) {
15+
// Resolve the absolute path
16+
const absolutePath = path.resolve(global.codecept_dir, filePath);
17+
18+
if (!absolutePath.startsWith(global.codecept_dir)) {
1319
return res.status(403).json({ error: 'Access denied. File must be within codecept directory' });
1420
}
1521

1622
try {
1723
// Check if file exists and get stats
18-
const stats = await fs.promises.stat(req.file);
24+
const stats = await fs.promises.stat(absolutePath);
1925

2026
// Check file size to prevent memory issues
2127
if (stats.size > MAX_FILE_SIZE) {
@@ -25,26 +31,38 @@ module.exports = async (req, res) => {
2531
});
2632
}
2733

28-
// Use streaming for better memory management
29-
const readStream = fs.createReadStream(req.file);
30-
31-
// Set appropriate headers
32-
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
33-
res.setHeader('Content-Length', stats.size);
34-
35-
// Handle stream errors
36-
readStream.on('error', (error) => {
37-
console.error('Error reading file:', error);
38-
if (!res.headersSent) {
39-
res.status(500).json({
40-
error: 'Failed to read file',
41-
message: error.message
42-
});
43-
}
44-
});
45-
46-
// Pipe the file directly to response to avoid loading into memory
47-
readStream.pipe(res);
34+
// For API requests that expect JSON response with source content
35+
if (req.body && req.body.path) {
36+
// Read file content and return as JSON (for frontend)
37+
const content = await fs.promises.readFile(absolutePath, 'utf8');
38+
39+
res.json({
40+
success: true,
41+
source: content,
42+
file: filePath
43+
});
44+
} else {
45+
// For legacy requests, stream the file directly (for backward compatibility)
46+
const readStream = fs.createReadStream(absolutePath);
47+
48+
// Set appropriate headers
49+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
50+
res.setHeader('Content-Length', stats.size);
51+
52+
// Handle stream errors
53+
readStream.on('error', (error) => {
54+
console.error('Error reading file:', error);
55+
if (!res.headersSent) {
56+
res.status(500).json({
57+
error: 'Failed to read file',
58+
message: error.message
59+
});
60+
}
61+
});
62+
63+
// Pipe the file directly to response
64+
readStream.pipe(res);
65+
}
4866

4967
} catch (error) {
5068
console.error('Error accessing file:', error);

lib/api/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ router.get('/steps', getSteps);
3232
router.get('/page-objects', getPageObjects);
3333
router.get('/snapshots/html/:id', getSnapshotHtml);
3434
router.get('/snapshots/screenshot/:id', getSnapshotImage);
35-
router.post('/file', getFile);
35+
router.post('/file', jsonParser, getFile);
3636

3737
router.get('/scenario-status', getScenarioStatus);
3838

src/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const store = require('./store').default;
3535
try {
3636
const response = await axios.get('/api/ports');
3737
const data = await response.data;
38-
wsConnection = `${window.location.protocol}//${window.location.hostname}:${data.wsPort}`;
38+
wsConnection = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.hostname}:${data.wsPort}`;
3939
} catch (err) {
4040
// Fallback to same origin if port fetch fails
4141
wsConnection = baseUrl.replace('http', 'ws');

0 commit comments

Comments
 (0)