api.php 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. <?php
  2. /**
  3. * Admin JSON API used by the browser-side uploader (assets/admin.js).
  4. *
  5. * POST JSON body: { "action": "...", ... } with X-CSRF-Token header.
  6. *
  7. * Actions:
  8. * presign { slug, name }
  9. * → presigned PUT URLs for the full-resolution original and its
  10. * browser-generated thumbnail.
  11. * register { slug, key, thumb, name, size }
  12. * → append an uploaded image to the gallery's JSON file.
  13. */
  14. require dirname(__DIR__, 2) . '/app/bootstrap.php';
  15. if (!auth_check()) {
  16. json_response(['error' => 'Not authenticated'], 401);
  17. }
  18. if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  19. json_response(['error' => 'POST only'], 405);
  20. }
  21. csrf_verify();
  22. $body = json_decode((string)file_get_contents('php://input'), true) ?: [];
  23. $action = (string)($body['action'] ?? '');
  24. $gallery = gallery_load((string)($body['slug'] ?? ''));
  25. if ($gallery === null) {
  26. json_response(['error' => 'Unknown gallery'], 404);
  27. }
  28. $slug = $gallery['slug'];
  29. switch ($action) {
  30. case 'presign':
  31. $name = basename((string)($body['name'] ?? ''));
  32. $name = preg_replace('/[^A-Za-z0-9._-]+/', '-', $name) ?: 'file';
  33. $name = substr($name, 0, 120);
  34. // Random prefix avoids overwrites when two files share a name.
  35. $token = random_token(6);
  36. $key = "galleries/$slug/originals/$token-$name";
  37. $thumb = "galleries/$slug/thumbs/$token-$name.jpg";
  38. json_response([
  39. 'key' => $key,
  40. 'thumb' => $thumb,
  41. 'put_original' => s3_presign_put($key, 3600),
  42. 'put_thumb' => s3_presign_put($thumb, 3600),
  43. ]);
  44. case 'register':
  45. $key = (string)($body['key'] ?? '');
  46. $thumb = (string)($body['thumb'] ?? '');
  47. if (!str_starts_with($key, "galleries/$slug/")) {
  48. json_response(['error' => 'Key does not belong to this gallery'], 400);
  49. }
  50. if ($thumb !== '' && !str_starts_with($thumb, "galleries/$slug/")) {
  51. json_response(['error' => 'Thumb key does not belong to this gallery'], 400);
  52. }
  53. // Re-load under current state to reduce lost updates between requests.
  54. $gallery = gallery_load($slug);
  55. $gallery['images'][] = [
  56. 'key' => $key,
  57. 'thumb' => $thumb !== '' ? $thumb : null,
  58. 'name' => substr((string)($body['name'] ?? basename($key)), 0, 200),
  59. 'size' => (int)($body['size'] ?? 0),
  60. ];
  61. gallery_save($gallery);
  62. json_response(['ok' => true, 'count' => count($gallery['images'])]);
  63. default:
  64. json_response(['error' => 'Unknown action'], 400);
  65. }