Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions apps/backend/src/routes/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,37 +106,46 @@ export function createUploadRouter(): Router {
});

// Get file info
router.get('/:fileId', (req, res) => {
router.get('/:fileId', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The application returns the full server-side file path (filePath) in the JSON response. This exposes the internal directory structure of the server to the client, which is a form of information disclosure. An attacker could use this information to better understand the server's environment and potentially facilitate other attacks.

size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}

const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
});

// Serve file
router.get('/:fileId/download', (req, res) => {
router.get('/:fileId/download', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
try {
await fs.promises.access(filePath, fs.constants.F_OK);
res.sendFile(filePath, (err) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The application serves uploaded files, including SVG files, using res.sendFile without restrictive security headers, which can lead to a Stored Cross-Site Scripting (XSS) vulnerability. SVG files can contain embedded <script> tags that execute in the user's browser. To mitigate this, consider setting a restrictive Content-Security-Policy (e.g., default-src 'none') or serving the files with Content-Disposition: attachment to force a download instead of inline rendering. Additionally, using fs.promises.access to check for file existence before res.sendFile can lead to a Time-of-check to time-of-use (TOCTOU) race condition, where the file could be altered or deleted after the check but before it's sent. A more robust approach is to call res.sendFile directly and handle errors in its callback.

if (err) next(err);
});
Comment on lines +138 to +142

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The access() pre-check adds an extra filesystem syscall and introduces a TOCTOU race: the file can disappear between access() and sendFile(), in which case sendFile will error and currently gets forwarded to next(err) (and may not map to a 404). Consider removing the access() check and instead handling err.code === 'ENOENT' inside the sendFile callback to return a 404, forwarding only non-ENOENT errors to next.

Copilot uses AI. Check for mistakes.
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}

res.sendFile(filePath);
});

// Handle multer errors (file type rejection, size limit, etc.)
Expand Down
60 changes: 60 additions & 0 deletions apps/backend/src/test/perf-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import express from 'express';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { createUploadRouter } from '../routes/upload.js';
import { config } from '../config.js';

async function runTest() {
console.log('Setting up benchmark...');
const app = express();
const uploadDir = path.join(config.projectDir, 'uploads');

if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}

// create dummy files
const dummyFiles: string[] = [];
for (let i = 0; i < 50; i++) {
const id = `testfile_${i}.txt`;
const p = path.join(uploadDir, id);
fs.writeFileSync(p, crypto.randomBytes(1024));
dummyFiles.push(id);
}

app.use('/api/upload', createUploadRouter());
const server = app.listen(3003, () => {
console.log('Server running on 3003');
});
Comment on lines +26 to +29

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script binds to a fixed port (3003), which can make local/CI runs flaky if the port is already in use. Consider listening on an ephemeral port (listen(0)) and using server.address() to build the request URLs.

Copilot uses AI. Check for mistakes.

await new Promise(res => setTimeout(res, 500));

console.log('Running sequential read benchmark on /api/upload/:id (info)...');

const start = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}`);
await res.json();
}
const duration = Date.now() - start;
console.log(`Baseline info time for 2000 requests: ${duration}ms`);

const startDownload = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}/download`);
await res.blob();
}
const durationDownload = Date.now() - startDownload;
console.log(`Baseline download time for 2000 requests: ${durationDownload}ms`);

server.close();
process.exit(0);
}

runTest().catch(err => {
console.error(err);
process.exit(1);
Comment on lines +31 to +59

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

server.close(); process.exit(0); can terminate the process before the server is fully closed and before any cleanup runs. Prefer awaiting server.close (wrap in a Promise) and performing cleanup in a finally block, then let the script exit naturally.

Suggested change
await new Promise(res => setTimeout(res, 500));
console.log('Running sequential read benchmark on /api/upload/:id (info)...');
const start = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}`);
await res.json();
}
const duration = Date.now() - start;
console.log(`Baseline info time for 2000 requests: ${duration}ms`);
const startDownload = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}/download`);
await res.blob();
}
const durationDownload = Date.now() - startDownload;
console.log(`Baseline download time for 2000 requests: ${durationDownload}ms`);
server.close();
process.exit(0);
}
runTest().catch(err => {
console.error(err);
process.exit(1);
try {
await new Promise(res => setTimeout(res, 500));
console.log('Running sequential read benchmark on /api/upload/:id (info)...');
const start = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}`);
await res.json();
}
const duration = Date.now() - start;
console.log(`Baseline info time for 2000 requests: ${duration}ms`);
const startDownload = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}/download`);
await res.blob();
}
const durationDownload = Date.now() - startDownload;
console.log(`Baseline download time for 2000 requests: ${durationDownload}ms`);
} finally {
await new Promise<void>((resolve, reject) => {
server.close(err => {
if (err) {
return reject(err);
}
resolve();
});
});
}
}
runTest().catch(err => {
console.error(err);
process.exitCode = 1;

Copilot uses AI. Check for mistakes.
});
Comment on lines +12 to +60

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmark creates dummy files in the configured uploads directory but never removes them. Please add cleanup (delete created files and/or use a temp subdirectory) so running this script doesn’t leave persistent artifacts in config.projectDir.

Suggested change
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// create dummy files
const dummyFiles: string[] = [];
for (let i = 0; i < 50; i++) {
const id = `testfile_${i}.txt`;
const p = path.join(uploadDir, id);
fs.writeFileSync(p, crypto.randomBytes(1024));
dummyFiles.push(id);
}
app.use('/api/upload', createUploadRouter());
const server = app.listen(3003, () => {
console.log('Server running on 3003');
});
await new Promise(res => setTimeout(res, 500));
console.log('Running sequential read benchmark on /api/upload/:id (info)...');
const start = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}`);
await res.json();
}
const duration = Date.now() - start;
console.log(`Baseline info time for 2000 requests: ${duration}ms`);
const startDownload = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}/download`);
await res.blob();
}
const durationDownload = Date.now() - startDownload;
console.log(`Baseline download time for 2000 requests: ${durationDownload}ms`);
server.close();
process.exit(0);
}
runTest().catch(err => {
console.error(err);
process.exit(1);
});
const dummyFiles: string[] = [];
let server: ReturnType<typeof app.listen> | null = null;
try {
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// create dummy files
for (let i = 0; i < 50; i++) {
const id = `testfile_${i}.txt`;
const p = path.join(uploadDir, id);
fs.writeFileSync(p, crypto.randomBytes(1024));
dummyFiles.push(id);
}
app.use('/api/upload', createUploadRouter());
server = app.listen(3003, () => {
console.log('Server running on 3003');
});
await new Promise(res => setTimeout(res, 500));
console.log('Running sequential read benchmark on /api/upload/:id (info)...');
const start = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}`);
await res.json();
}
const duration = Date.now() - start;
console.log(`Baseline info time for 2000 requests: ${duration}ms`);
const startDownload = Date.now();
for (let i = 0; i < 2000; i++) {
const fileId = dummyFiles[i % dummyFiles.length];
const res = await fetch(`http://localhost:3003/api/upload/${fileId}/download`);
await res.blob();
}
const durationDownload = Date.now() - startDownload;
console.log(`Baseline download time for 2000 requests: ${durationDownload}ms`);
} finally {
if (server) {
server.close();
}
for (const id of dummyFiles) {
const filePath = path.join(uploadDir, id);
if (fs.existsSync(filePath)) {
try {
fs.unlinkSync(filePath);
} catch (e) {
console.error(`Failed to delete dummy file ${filePath}:`, e);
}
}
}
}
}
runTest()
.then(() => {
process.exit(0);
})
.catch(err => {
console.error(err);
process.exit(1);
});

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +60

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This performance test script creates dummy files but doesn't clean them up after running. This will leave artifacts in the uploads directory. Furthermore, if an error occurs during the test, server.close() is not called, which could leave a running process. It's best practice for tests to clean up their own resources. I suggest wrapping the test logic in a try...finally block to ensure dummy files are deleted and the server is closed, regardless of whether the test succeeds or fails.

async function runTest() {
    console.log('Setting up benchmark...');
    const app = express();
    const uploadDir = path.join(config.projectDir, 'uploads');

    if (!fs.existsSync(uploadDir)) {
        fs.mkdirSync(uploadDir, { recursive: true });
    }

    // create dummy files
    const dummyFiles: string[] = [];
    for (let i = 0; i < 50; i++) {
        const id = `testfile_${i}.txt`;
        const p = path.join(uploadDir, id);
        fs.writeFileSync(p, crypto.randomBytes(1024));
        dummyFiles.push(id);
    }

    app.use('/api/upload', createUploadRouter());
    const server = app.listen(3003, () => {
        console.log('Server running on 3003');
    });

    try {
        await new Promise(res => setTimeout(res, 500));

        console.log('Running sequential read benchmark on /api/upload/:id (info)...');

        const start = Date.now();
        for (let i = 0; i < 2000; i++) {
            const fileId = dummyFiles[i % dummyFiles.length];
            const res = await fetch(`http://localhost:3003/api/upload/${fileId}`);
            await res.json();
        }
        const duration = Date.now() - start;
        console.log(`Baseline info time for 2000 requests: ${duration}ms`);

        const startDownload = Date.now();
        for (let i = 0; i < 2000; i++) {
            const fileId = dummyFiles[i % dummyFiles.length];
            const res = await fetch(`http://localhost:3003/api/upload/${fileId}/download`);
            await res.blob();
        }
        const durationDownload = Date.now() - startDownload;
        console.log(`Baseline download time for 2000 requests: ${durationDownload}ms`);
    } finally {
        console.log('Cleaning up...');
        server.close();
        for (const fileId of dummyFiles) {
            fs.unlinkSync(path.join(uploadDir, fileId));
        }
    }
}

runTest().then(() => {
    process.exit(0);
}).catch(err => {
    console.error(err);
    process.exit(1);
});

28 changes: 28 additions & 0 deletions code_review_diff.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<<<<<<< SEARCH
if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}

const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
=======
try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
>>>>>>> REPLACE
Comment on lines +1 to +28

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code_review_diff.patch contains patch-tool markers/conflict-style delimiters (<<<<<<<, =======, >>>>>>>) and doesn’t appear to be a source file used by the project. Please remove it from the PR to avoid accidentally checking in review artifacts.

Suggested change
<<<<<<< SEARCH
if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}
const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
=======
try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
>>>>>>> REPLACE

Copilot uses AI. Check for mistakes.
10 changes: 10 additions & 0 deletions fix_fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import fs from 'fs';
import path from 'path';

let fileObjMap: Record<string, string> = {
"const filePath = path.join(uploadDir, req.params.fileId);": `const filePath = path.join(uploadDir, req.params.fileId);

try { await fs.promises.access(filePath, fs.constants.R_OK); } catch { return res.status(404).json({ error: 'File not found' }); }
res.sendFile(filePath);
`
};
Comment on lines +1 to +10

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix_fs.ts looks like an incomplete/unused helper snippet (a hard-coded string replacement map) rather than code that’s part of the backend/app. Please remove this file from the PR to avoid shipping dead or confusing code.

Suggested change
import fs from 'fs';
import path from 'path';
let fileObjMap: Record<string, string> = {
"const filePath = path.join(uploadDir, req.params.fileId);": `const filePath = path.join(uploadDir, req.params.fileId);
try { await fs.promises.access(filePath, fs.constants.R_OK); } catch { return res.status(404).json({ error: 'File not found' }); }
res.sendFile(filePath);
`
};

Copilot uses AI. Check for mistakes.
83 changes: 83 additions & 0 deletions patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const fs = require('fs');

let content = fs.readFileSync('apps/backend/src/routes/upload.ts', 'utf8');

content = content.replace(
` // Get file info
router.get('/:fileId', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}

const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
});

// Serve file
router.get('/:fileId/download', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}

res.sendFile(filePath);
});`,
` // Get file info
router.get('/:fileId', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});

// Serve file
router.get('/:fileId/download', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

try {
await fs.promises.access(filePath, fs.constants.F_OK);
res.sendFile(filePath, (err) => {
if (err) next(err);
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});`
);

fs.writeFileSync('apps/backend/src/routes/upload.ts', content);
Comment on lines +1 to +83

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a one-off local patching script used to rewrite apps/backend/src/routes/upload.ts, not application code. Please remove patch.js from the PR (and avoid committing generated patch helpers).

Suggested change
const fs = require('fs');
let content = fs.readFileSync('apps/backend/src/routes/upload.ts', 'utf8');
content = content.replace(
` // Get file info
router.get('/:fileId', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}
const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
});
// Serve file
router.get('/:fileId/download', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}
res.sendFile(filePath);
});`,
` // Get file info
router.get('/:fileId', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});
// Serve file
router.get('/:fileId/download', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
try {
await fs.promises.access(filePath, fs.constants.F_OK);
res.sendFile(filePath, (err) => {
if (err) next(err);
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});`
);
fs.writeFileSync('apps/backend/src/routes/upload.ts', content);
// This file previously contained a one-off local patch script used to rewrite
// `apps/backend/src/routes/upload.ts`. It has been intentionally left empty
// to avoid committing generated or ad-hoc patch helpers to the codebase.

Copilot uses AI. Check for mistakes.
83 changes: 83 additions & 0 deletions patch2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const fs = require('fs');

let content = fs.readFileSync('apps/backend/src/routes/upload.ts', 'utf8');

content = content.replace(
` // Get file info
router.get('/:fileId', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}

const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
});

// Serve file
router.get('/:fileId/download', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}

res.sendFile(filePath);
});`,
` // Get file info
router.get('/:fileId', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});

// Serve file
router.get('/:fileId/download', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);

try {
await fs.promises.access(filePath, fs.constants.F_OK);
res.sendFile(filePath, (err) => {
if (err) next(err);
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});`
);

fs.writeFileSync('apps/backend/src/routes/upload.ts', content);
Comment on lines +1 to +83

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a one-off local patching script used to rewrite apps/backend/src/routes/upload.ts, not application code. Please remove patch2.js from the PR (and avoid committing generated patch helpers).

Suggested change
const fs = require('fs');
let content = fs.readFileSync('apps/backend/src/routes/upload.ts', 'utf8');
content = content.replace(
` // Get file info
router.get('/:fileId', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}
const stats = fs.statSync(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
});
// Serve file
router.get('/:fileId/download', (req, res) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
if (!fs.existsSync(filePath)) {
return res.status(404).json({ error: 'File not found' });
}
res.sendFile(filePath);
});`,
` // Get file info
router.get('/:fileId', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
try {
const stats = await fs.promises.stat(filePath);
res.json({
fileId: req.params.fileId,
filePath,
size: stats.size,
created: stats.birthtime
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});
// Serve file
router.get('/:fileId/download', async (req, res, next) => {
if (!isSafeFileId(req.params.fileId)) {
return res.status(400).json({ error: 'Invalid file ID' });
}
const filePath = path.join(uploadDir, req.params.fileId);
try {
await fs.promises.access(filePath, fs.constants.F_OK);
res.sendFile(filePath, (err) => {
if (err) next(err);
});
} catch (err: any) {
if (err.code === 'ENOENT') {
return res.status(404).json({ error: 'File not found' });
}
next(err);
}
});`
);
fs.writeFileSync('apps/backend/src/routes/upload.ts', content);
// This file previously contained a one-off local patching script
// to rewrite `apps/backend/src/routes/upload.ts`.
// It has been intentionally neutralized and left as a no-op to
// avoid committing generated patch helpers into the codebase.
'use strict';
// No-op module: retained only so that any references to `patch2.js`
// continue to work without performing any file modifications.
module.exports = {};

Copilot uses AI. Check for mistakes.
1 change: 1 addition & 0 deletions server.pid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2247

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

server.pid appears to be a local runtime artifact (PID file) and shouldn’t be committed to the repo. Please remove it from the PR and consider adding server.pid to .gitignore to prevent future accidental commits.

Suggested change
2247

Copilot uses AI. Check for mistakes.
Loading