Skip to content
This repository was archived by the owner on Apr 8, 2026. It is now read-only.

Commit b2551fb

Browse files
committed
fix(download): improve error handling and logging for game file downloads
1 parent b42b38f commit b2551fb

1 file changed

Lines changed: 27 additions & 26 deletions

File tree

src/controllers/GameController.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ export class Games {
9797
}
9898
}
9999

100+
private logUnexpectedStatus(req: Request, statusCode: number, message: string) {
101+
console.warn(`Unexpected status code ${statusCode} for request ${req.method} ${req.originalUrl}: ${message}`);
102+
}
103+
100104
@httpGet('/')
101105
public async listGames(req: Request, res: Response) {
102106
try {
@@ -399,35 +403,32 @@ export class Games {
399403
const [_, owner, repo] = githubMatch;
400404
downloadUrl = `https://github.com/${owner}/${repo}/archive/refs/heads/main.zip`;
401405
}
406+
const headers: any = {};
402407
if (req.headers.range) {
403-
404-
const headers: any = {};
405-
if (req.headers.range) {
406-
headers.Range = req.headers.range;
407-
}
408-
const fileRes = await fetch(downloadUrl, { headers });
409-
if (!fileRes.ok) return res.status(502).send({ message: 'Failed to fetch game file' });
410-
const sanitizedFileName = encodeURIComponent(game.name.replace(/[^a-zA-Z0-9-_\.]/g, '_'));
411-
res.setHeader('Content-Disposition', `attachment; filename="${sanitizedFileName}.zip"`);
412-
res.setHeader('Content-Type', fileRes.headers.get('content-type') || 'application/octet-stream');
413-
const contentLength = fileRes.headers.get('content-length');
414-
if (contentLength) res.setHeader('Content-Length', contentLength);
415-
const acceptRanges = fileRes.headers.get('accept-ranges');
416-
if (acceptRanges) res.setHeader('Accept-Ranges', acceptRanges);
417-
if (fileRes.headers.get('content-range')) res.setHeader('Content-Range', fileRes.headers.get('content-range')!);
418-
res.status(fileRes.status);
419-
if (fileRes.body) {
420-
try {
421-
await streamPipeline(fileRes.body, res);
422-
} catch (err) {
423-
res.status(500).send({ message: 'Error streaming the file.' });
424-
}
425-
} else {
426-
res.status(500).send({ message: 'Response body is empty.' });
408+
headers.Range = req.headers.range;
409+
}
410+
const fileRes = await fetch(downloadUrl, { headers });
411+
if (!fileRes.ok) {
412+
this.logUnexpectedStatus(req, fileRes.status, 'Failed to fetch game file');
413+
return res.status(fileRes.status).send({ message: 'Failed to fetch game file' });
414+
}
415+
const sanitizedFileName = encodeURIComponent(game.name.replace(/[^a-zA-Z0-9-_\.]/g, '_'));
416+
res.setHeader('Content-Disposition', `attachment; filename="${sanitizedFileName}.zip"`);
417+
res.setHeader('Content-Type', fileRes.headers.get('content-type') || 'application/octet-stream');
418+
const contentLength = fileRes.headers.get('content-length');
419+
if (contentLength) res.setHeader('Content-Length', contentLength);
420+
const acceptRanges = fileRes.headers.get('accept-ranges');
421+
if (acceptRanges) res.setHeader('Accept-Ranges', acceptRanges);
422+
if (fileRes.headers.get('content-range')) res.setHeader('Content-Range', fileRes.headers.get('content-range')!);
423+
res.status(fileRes.status);
424+
if (fileRes.body) {
425+
try {
426+
await streamPipeline(fileRes.body, res);
427+
} catch (err) {
428+
res.status(500).send({ message: 'Error streaming the file.' });
427429
}
428430
} else {
429-
430-
res.redirect(downloadUrl);
431+
res.status(500).send({ message: 'Response body is empty.' });
431432
}
432433
} catch (error) {
433434
handleError(res, error, 'Error downloading game');

0 commit comments

Comments
 (0)