env('MEDIA_DISK', 'public'), /* |-------------------------------------------------------------------------- | Derived versions |-------------------------------------------------------------------------- | | Widths generated for each image, in pixels. An image is never scaled up: | a 900px original produces the 400 and 800 versions and stops there, so a | small upload cannot be blown up into a blurry "large" one. | */ 'widths' => [400, 800, 1200, 1600], /* | Formats to generate besides the original. AVIF is listed because it is | worth having where it exists, but it is only produced when the running | PHP can encode it — the generator checks rather than assumes. */ 'formats' => ['avif', 'webp'], 'quality' => [ 'webp' => 82, 'avif' => 55, 'jpeg' => 82, ], /* |-------------------------------------------------------------------------- | Placeholder |-------------------------------------------------------------------------- | | A tiny blurred version inlined as a data URI, so the frontend can paint | something in the right colours while the real image loads. Kept small on | purpose: it travels inside every JSON response that mentions the image. | */ 'placeholder' => [ 'enabled' => true, 'width' => 20, ], /* |-------------------------------------------------------------------------- | Limits |-------------------------------------------------------------------------- | | Pixel ceiling for processing. Above it the image is recorded with its | dimensions but no derived versions: resizing a 100-megapixel file costs | more memory than a small panel is worth, and it is nearly always a | mistake rather than an intention. | */ 'max_pixels' => (int) env('MEDIA_MAX_PIXELS', 50_000_000), // Formats that are copied through untouched. Resampling a vector loses the // point of it. 'passthrough_mimes' => ['image/svg+xml', 'image/gif'], /* |-------------------------------------------------------------------------- | Uploads through the API |-------------------------------------------------------------------------- | | POST /api/v1/media, which the MCP server uses to put an image on a page. | | SVG is accepted because it is what a logo is, but never as sent: an SVG | is a document that can carry script, served from this application's | domain, and this endpoint also fetches from arbitrary URLs. It goes | through enshrined/svg-sanitize first — scripts, event handlers and | remote references removed — and Caddy serves stored SVGs under a | Content-Security-Policy that would stop a script anyway. | | The size ceiling sits under php.ini's `upload_max_filesize` (24M) so the | answer is a readable 422 rather than PHP silently dropping the file. | */ 'upload' => [ 'mimes' => ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/avif', 'image/svg+xml'], 'max_kb' => (int) env('MEDIA_UPLOAD_MAX_KB', 20 * 1024), // What the image is for, and so where it lands — the same directories // the panel's own upload fields use — and whose permission it takes. 'for' => [ 'pages' => 'pages', 'technologies' => 'technologies', 'projects' => 'projects', 'members' => 'members', 'clients' => 'clients', 'testimonials' => 'testimonials', ], // Fetching from a URL: how long to wait, in seconds. The request that // triggered it is waiting too. 'remote_timeout' => (int) env('MEDIA_REMOTE_TIMEOUT', 15), ], ];