| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| import {
|
| normalizeRequestBodyLimitMb,
|
| parseRequestBodyLimitBytes,
|
| requestBodyLimitBytesToMb,
|
| requestBodyLimitMbToBytes,
|
| } from "../constants/bodySize";
|
|
|
|
|
| export const MAX_BODY_BYTES_IMPORT = 100 * 1024 * 1024;
|
|
|
|
|
| export const MAX_BODY_BYTES_AUDIO = 100 * 1024 * 1024;
|
|
|
|
|
| export const MAX_BODY_BYTES_FILE = 500 * 1024 * 1024;
|
|
|
|
|
| export const MAX_BODY_BYTES = parseRequestBodyLimitBytes(process.env.MAX_BODY_SIZE_BYTES);
|
|
|
| type BodySizeRule = { prefix: string; limit: number };
|
|
|
| const ROUTE_LIMITS: BodySizeRule[] = [
|
| { prefix: "/api/db-backups/import", limit: MAX_BODY_BYTES_IMPORT },
|
| { prefix: "/api/v1/audio/transcriptions", limit: MAX_BODY_BYTES_AUDIO },
|
| { prefix: "/api/v1/files", limit: MAX_BODY_BYTES_FILE },
|
| ];
|
|
|
| export function getDefaultRequestBodyLimitMb(): number {
|
| return requestBodyLimitBytesToMb(MAX_BODY_BYTES);
|
| }
|
|
|
| export function getConfiguredBodySizeLimitBytes(settings?: Record<string, unknown>): number {
|
| const configuredMb = normalizeRequestBodyLimitMb(settings?.maxBodySizeMb);
|
| return configuredMb === null ? MAX_BODY_BYTES : requestBodyLimitMbToBytes(configuredMb);
|
| }
|
|
|
| |
| |
|
|
| export function getBodySizeLimit(pathname: string, settings?: Record<string, unknown>): number {
|
| const configuredLimit = getConfiguredBodySizeLimitBytes(settings);
|
| const customRule = ROUTE_LIMITS.find((rule) => pathname.startsWith(rule.prefix));
|
| return customRule ? Math.max(customRule.limit, configuredLimit) : configuredLimit;
|
| }
|
|
|
| |
| |
| |
|
|
| export function checkBodySize(request: Request, limit: number = MAX_BODY_BYTES): Response | null {
|
| const contentLength = request.headers.get("content-length");
|
|
|
| if (contentLength) {
|
| const bytes = Number.parseInt(contentLength, 10);
|
| if (!Number.isNaN(bytes) && bytes > limit) {
|
| return new Response(
|
| JSON.stringify({
|
| error: {
|
| message: `Request body too large. Maximum allowed: ${formatBytes(limit)}`,
|
| type: "payload_too_large",
|
| code: "PAYLOAD_TOO_LARGE",
|
| },
|
| }),
|
| {
|
| status: 413,
|
| headers: {
|
| "Content-Type": "application/json",
|
| },
|
| }
|
| );
|
| }
|
| }
|
|
|
| return null;
|
| }
|
|
|
|
|
| function formatBytes(bytes: number): string {
|
| if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;
|
| if (bytes >= 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
| return `${bytes} bytes`;
|
| }
|
|
|