'Not authenticated'], 401); } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { json_response(['error' => 'POST only'], 405); } csrf_verify(); // Nothing here is slow, but a drag over many images fires these back to back // and PHP holds the session file exclusively for the whole request. session_write_close(); $slug = (string)($_POST['slug'] ?? ''); $gallery = gallery_load($slug); if ($gallery === null) { json_response(['error' => 'Unknown gallery'], 404); } if (($_POST['action'] ?? '') !== 'assign') { json_response(['error' => 'Unknown action'], 400); } $topic = (string)($_POST['topic'] ?? ''); // Checked against this gallery's topics, so a stale page cannot write a // reference to one that has since been deleted. gallery_assign_topic() checks // again under its lock, where the answer is authoritative. if ($topic !== '' && !isset(gallery_topic_map($gallery)[$topic])) { json_response(['error' => 'Unknown topic'], 400); } if (!gallery_assign_topic($slug, (string)($_POST['key'] ?? ''), $topic)) { json_response(['error' => 'Unknown image'], 404); } json_response(['ok' => true]);