mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 07:59:42 +02:00
feat(files): raise the download cap to 2GB and stream /api/download
The 50MB cap on file-raw, the attachment /raw route and /api/download was memory protection for a `readFile()` that no longer exists: file-raw and /raw were rewritten to stream through `sendFileBody()` and answer Range requests, so size costs a read stream rather than RSS (measured: a 600MB download moved peak RSS by ~37MB). All the cap still did was refuse legitimate downloads of build artifacts, videos and archives. It is now MAX_FILE_DOWNLOAD_BYTES in config/buffer-limits.ts, default 2GB, env CODEMAN_MAX_DOWNLOAD_BYTES, 0 = unlimited. `parseByteLimitEnv()` is separate from the `parseInt(...) || default` idiom used elsewhere in that file precisely because that idiom reads 0 as falsy and would silently restore the default for the one value that means "no limit". /api/download was the last route that really did buffer the whole file. It now shares sendFileBody() with the other two, so it streams, advertises Accept-Ranges, and is resumable. Its Content-Disposition also goes through buildContentDisposition() rather than raw interpolation. Refusals move from 400 to 413 across all three, which is the correct status for the case; with the cap at 2GB it is a path almost nothing reaches now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -112,3 +112,50 @@ export const FILE_PEEK_BYTES = 8 * 1024 - 1; // 8KB (inclusive end offset)
|
||||
* Override: CODEMAN_MAX_PASTE_IMAGE_BYTES (bytes)
|
||||
*/
|
||||
export const MAX_PASTE_IMAGE_BYTES = parseInt(process.env.CODEMAN_MAX_PASTE_IMAGE_BYTES || '') || 50 * 1024 * 1024; // 50MB
|
||||
|
||||
// ============================================================================
|
||||
// File Download Limits
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Parse a byte-limit env var, where `0` explicitly means "no limit".
|
||||
*
|
||||
* The `parseInt(...) || default` idiom used elsewhere in this file cannot
|
||||
* express that: it treats 0 as falsy and silently restores the default.
|
||||
*/
|
||||
function parseByteLimitEnv(raw: string | undefined, fallback: number): number {
|
||||
if (raw === undefined || raw.trim() === '') return fallback;
|
||||
const parsed = Number.parseInt(raw, 10);
|
||||
if (!Number.isFinite(parsed) || parsed < 0) return fallback;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum size (bytes) of a file served by the raw/download file routes:
|
||||
* `GET /api/sessions/:id/file-raw` (the Files panel's download link and the
|
||||
* file-preview overlay), the attachment `/raw` route, and `GET /api/download`.
|
||||
*
|
||||
* ⚠️ This is a sanity bound, NOT memory protection. All three bodies are
|
||||
* STREAMED and `Range`-aware (`sendFileBody` in file-routes.ts), so a large
|
||||
* file costs one read stream rather than its size in RSS. The historical 50MB
|
||||
* cap predates that streaming rewrite and its "prevent memory exhaustion"
|
||||
* comment described a `readFile()` that no longer exists — all it did was
|
||||
* refuse legitimate downloads of build artifacts, videos and archives.
|
||||
*
|
||||
* Set `CODEMAN_MAX_DOWNLOAD_BYTES=0` to remove the cap entirely.
|
||||
* Override: CODEMAN_MAX_DOWNLOAD_BYTES (bytes)
|
||||
*/
|
||||
export const MAX_FILE_DOWNLOAD_BYTES = parseByteLimitEnv(
|
||||
process.env.CODEMAN_MAX_DOWNLOAD_BYTES,
|
||||
2 * 1024 * 1024 * 1024 // 2GB
|
||||
);
|
||||
|
||||
/** True when `size` exceeds the download cap (a cap of 0 means unlimited). */
|
||||
export function exceedsDownloadLimit(size: number): boolean {
|
||||
return MAX_FILE_DOWNLOAD_BYTES > 0 && size > MAX_FILE_DOWNLOAD_BYTES;
|
||||
}
|
||||
|
||||
/** Human-readable "File too large (…)" message for a refused download. */
|
||||
export function downloadTooLargeMessage(size: number): string {
|
||||
return `File too large (${Math.round(size / 1024 / 1024)}MB > ${Math.round(MAX_FILE_DOWNLOAD_BYTES / 1024 / 1024)}MB limit). Raise or remove it with CODEMAN_MAX_DOWNLOAD_BYTES (0 = unlimited).`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user