diff --git a/apps/dokploy/pages/api/[...trpc].ts b/apps/dokploy/pages/api/[...trpc].ts index 83ff9b050..dbad15922 100644 --- a/apps/dokploy/pages/api/[...trpc].ts +++ b/apps/dokploy/pages/api/[...trpc].ts @@ -1,4 +1,8 @@ -import { validateRequest } from "@dokploy/server"; +import { + OPENAPI_MAX_JSON_BODY_SIZE, + OPENAPI_MAX_UPLOAD_SIZE, + validateRequest, +} from "@dokploy/server"; import { createOpenApiNextHandler } from "@dokploy/trpc-openapi"; import type { NextApiRequest, NextApiResponse } from "next"; import { appRouter } from "@/server/api/root"; @@ -12,10 +16,31 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return; } + // getMultipartBody doesn't accept maxBodySize, so we cap it here instead. + const contentLength = Number(req.headers["content-length"]); + const isMultipart = req.headers["content-type"]?.startsWith( + "multipart/form-data", + ); + + if (isMultipart && !Number.isFinite(contentLength)) { + res.status(411).json({ message: "Content-Length required" }); + return; + } + + const limit = isMultipart + ? OPENAPI_MAX_UPLOAD_SIZE + : OPENAPI_MAX_JSON_BODY_SIZE; + + if (Number.isFinite(contentLength) && contentLength > limit) { + res.status(413).json({ message: "Payload too large" }); + return; + } + // @ts-ignore return createOpenApiNextHandler({ router: appRouter, createContext: createTRPCContext, + maxBodySize: OPENAPI_MAX_JSON_BODY_SIZE, onError: process.env.NODE_ENV === "development" ? ({ path, error }: { path: string | undefined; error: Error }) => { @@ -28,3 +53,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { }; export default handler; + +export const config = { + api: { + bodyParser: false, + }, +}; diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 51ffeb8c4..c303041f3 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -13,6 +13,28 @@ export const DOKPLOY_DOCKER_PORT = process.env.DOKPLOY_DOCKER_PORT export const CLEANUP_CRON_JOB = "50 23 * * *"; +// Body size limits for the OpenAPI catch-all route (pages/api/[...trpc].ts). +const parseByteSize = (envVar: string, fallback: number): number => { + const raw = process.env[envVar]; + if (!raw) return fallback; + const parsed = Number(raw); + if (!Number.isInteger(parsed) || parsed <= 0) { + console.warn(`Invalid ${envVar}="${raw}", using default ${fallback}`); + return fallback; + } + return parsed; +}; + +export const OPENAPI_MAX_JSON_BODY_SIZE = parseByteSize( + "OPENAPI_MAX_JSON_BODY_SIZE", + 10 * 1024 * 1024, // 10mb +); + +export const OPENAPI_MAX_UPLOAD_SIZE = parseByteSize( + "OPENAPI_MAX_UPLOAD_SIZE", + 1024 * 1024 * 1024, // 1gb +); + type DockerSocketCandidate = { label: string; path: string;