Atlas modal: pose toggle + wiki descriptions

avian/api/cutout.php: read ?pose=N parameter. pose=1 (default) serves
<slug>.png, pose=2 serves <slug>-2.png, etc. Pose >1 missing falls
back to pose-1 instead of skipping to the photo cutout so the flight
tab still shows something recognisable. Pose clamped to 1-99 to
block path-traversal payloads.

avian/api/wiki.php (new): Wikipedia REST summary proxy. Returns
{extract, thumbnail, title}. Thumbnail host pinned to wikimedia.org/
wikipedia.org to block SSRF. Cached for 24h.

avian/frontend/apt.js: fetch wiki descriptions from ./avian/api/wiki.php
(was hitting the worker's /api/wiki.json which 404'd in our overlay).
This commit is contained in:
Twarner491
2026-05-28 12:16:08 -07:00
parent 0105fb54f0
commit 35eed2a885
3 changed files with 74 additions and 3 deletions
+17 -2
View File
@@ -33,6 +33,12 @@ if (!preg_match('/^[A-Za-z]{2,40}(?:[ ][a-z]{2,40}){1,3}$/', $sci)) {
$slug = preg_replace('/[^a-z0-9]+/', '-', strtolower($sci));
$slug = trim((string)$slug, '-');
// pose=1 (default) is perched. pose=2 is flight. Clamp to a two-digit
// positive integer so a malformed ?pose= can't break the path.
$pose = (int)($_GET['pose'] ?? 1);
if ($pose < 1 || $pose > 99) $pose = 1;
$poseSuffix = $pose === 1 ? '' : "-$pose";
function serve_png(string $path): void {
header('Content-Type: image/png');
header('Cache-Control: public, max-age=86400');
@@ -41,11 +47,20 @@ function serve_png(string $path): void {
exit;
}
// 1. Bundled illustration (the kachō-e PNG the repo ships with).
$bundled = dirname(__DIR__) . "/assets/illustrations/$slug.png";
// 1. Bundled illustration with pose suffix (the kachō-e PNG the repo
// ships with). 450+ species cover both perched + flight.
$bundled = dirname(__DIR__) . "/assets/illustrations/{$slug}{$poseSuffix}.png";
if (is_file($bundled) && filesize($bundled) > 1024) {
serve_png($bundled);
}
// Pose-2 missing? Fall back to pose-1 so the flight tab still shows
// the perched render instead of breaking to the photo fallback.
if ($pose !== 1) {
$fallback = dirname(__DIR__) . "/assets/illustrations/$slug.png";
if (is_file($fallback) && filesize($fallback) > 1024) {
serve_png($fallback);
}
}
// 2. Bundled cutout (background-removed photo, fallback for species
// without an illustration).
$cutout = dirname(__DIR__) . "/assets/cutouts/$slug.png";