diff --git a/next.config.ts b/next.config.ts index 10eb7a6..a113923 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,67 +4,44 @@ const nextConfig: NextConfig = { experimental: { optimizePackageImports: ["lucide-react", "@radix-ui/react-icons"], serverActions: { - // Job files bucket caps individual files at 30MB; raise overall body - // limit to allow batch uploads. - bodySizeLimit: "100mb", + // Job files bucket caps individual files at 30MB; allow batch uploads + // (multipart overhead + ~16 files of 30MB worst case). + bodySizeLimit: "500mb", }, + // Next 16 renamed `middlewareClientMaxBodySize` to `proxyClientMaxBodySize` + // (middleware.ts → proxy.ts). Default 10MB gates every body that flows + // through our auth proxy — without this override multipart uploads exceed + // the cap and the parser dies with "Unexpected end of form". + proxyClientMaxBodySize: "500mb", }, turbopack: {}, - // Image optimization images: { remotePatterns: [ - { - protocol: 'https', - hostname: 'ui.shadcn.com', - }, - { - protocol: 'https', - hostname: 'images.unsplash.com', - }, + { protocol: "https", hostname: "ui.shadcn.com" }, + { protocol: "https", hostname: "images.unsplash.com" }, ], - formats: ['image/webp', 'image/avif'], + formats: ["image/webp", "image/avif"], }, - // Headers for better security and performance async headers() { return [ { - source: '/(.*)', + source: "/(.*)", headers: [ - { - key: 'X-Frame-Options', - value: 'DENY', - }, - { - key: 'X-Content-Type-Options', - value: 'nosniff', - }, - { - key: 'Referrer-Policy', - value: 'origin-when-cross-origin', - }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "Referrer-Policy", value: "origin-when-cross-origin" }, ], }, ]; }, - // Redirects for better SEO async redirects() { return [ - { - source: '/home', - destination: '/dashboard', - permanent: true, - }, + { source: "/home", destination: "/dashboard", permanent: true }, ]; }, }; -// Next 16 caps middleware-passed request bodies at 10MB by default. Our auth -// middleware sees every request including job-file uploads — without this -// override, multipart uploads larger than 10MB return "Unexpected end of -// form". The key isn't in NextConfig's TS types yet (Next 16.1). -(nextConfig as NextConfig & { middlewareClientMaxBodySize?: string }).middlewareClientMaxBodySize = "100mb"; - export default nextConfig; diff --git a/src/app/(dashboard)/jobs/[jobId]/components/job-files-panel.tsx b/src/app/(dashboard)/jobs/[jobId]/components/job-files-panel.tsx index 01e98d0..933eebe 100644 --- a/src/app/(dashboard)/jobs/[jobId]/components/job-files-panel.tsx +++ b/src/app/(dashboard)/jobs/[jobId]/components/job-files-panel.tsx @@ -63,6 +63,9 @@ export function JobFilesPanel({ ); } +const MAX_FILE_BYTES = 30 * 1024 * 1024; +const MAX_BATCH_BYTES = 400 * 1024 * 1024; // leaves headroom under the 500MB proxy cap + function UploadForm({ jobId }: { jobId: string }) { const [state, action, pending] = useActionState( uploadJobFilesAction, @@ -82,6 +85,11 @@ function UploadForm({ jobId }: { jobId: string }) { } }, [state]); + const totalBytes = selected.reduce((s, f) => s + f.size, 0); + const overSize = selected.find((f) => f.size > MAX_FILE_BYTES); + const overBatch = totalBytes > MAX_BATCH_BYTES; + const blocked = Boolean(overSize) || overBatch; + return (
- {selected.length > 0 - ? `${selected.length} dosya seçildi (${formatSize(selected.reduce((s, f) => s + f.size, 0))})` - : "Tarama (STL/OBJ), görsel veya PDF — max 30MB / dosya"} + {selected.length > 0 ? ( + overSize ? ( + + {overSize.name} 30MB'tan büyük (her dosya maksimum 30MB). + + ) : overBatch ? ( + + Toplam {formatSize(totalBytes)} — 400MB sınırını aşıyor. Daha az dosya seçin. + + ) : ( + <> + {selected.length} dosya seçildi ({formatSize(totalBytes)}) + + ) + ) : ( + "Tarama (STL/OBJ), görsel veya PDF — max 30MB / dosya" + )} -