| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 |
- <?php
- /**
- * Admin upload endpoint used by the browser-side uploader (assets/admin.js).
- *
- * One multipart POST per image (X-CSRF-Token header, fields below); the webhost
- * streams the file straight to S3 and appends it to the gallery's JSON file.
- * Keeping it to a single file per request means each PHP process only ever
- * handles one image, so a multi-gigabyte gallery upload never trips
- * post_max_size / max_execution_time — only the largest single image does.
- *
- * Fields:
- * slug gallery slug
- * original the full-resolution file (required, stored unmodified)
- * thumb browser-generated JPEG thumbnail (optional; absent for RAW/video)
- *
- * The browser never receives an S3 URL or any credential for writing.
- */
- require dirname(__DIR__) . '/app/bootstrap.php';
- if (!auth_check()) {
- json_response(['error' => 'Not authenticated'], 401);
- }
- if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
- json_response(['error' => 'POST only'], 405);
- }
- csrf_verify();
- // One image per request; a single file may still be large, so lift the time cap.
- @set_time_limit(0);
- // When a request body exceeds post_max_size, PHP discards $_POST and $_FILES
- // entirely — surface that as a clear 413 instead of a misleading "no file".
- if ((int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 0 && !$_POST && !$_FILES) {
- json_response(['error' => 'Upload exceeds the server post_max_size limit'], 413);
- }
- $gallery = gallery_load((string)($_POST['slug'] ?? ''));
- if ($gallery === null) {
- json_response(['error' => 'Unknown gallery'], 404);
- }
- // Trusted admin path: any file type is allowed (imagesOnly stays false).
- [$status, $payload] = gallery_store_s3_upload(
- $gallery,
- $_FILES['original'] ?? null,
- $_FILES['thumb'] ?? null
- );
- json_response($payload, $status);
|