Files
Art-gallery/Documentation/API.md
T
Danila KhodjaefandCursor bf7db9b25e Add influence rework, image checkup, debug mode, and fetched paintings.
Support artist and movement influence links with web discovery, a developer checkup table with gallery/detail thumbnails, and debug image search with fix-it workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-20 10:29:43 +03:00

6.1 KiB
Raw Blame History

Art Gallery — REST API

Base URL in development:

  • Direct: http://localhost:3001
  • Via Vite proxy: http://localhost:5173 (same paths)

All JSON responses use Content-Type: application/json. Errors return { "error": "message" } with an appropriate HTTP status.

Static images are served at /images/<relative-path> from IMAGE_DIR.


GET /api/bounds

Returns the overall timeline year range used to initialise the zoomable timeline.

Response

{
  "min_year": -800,
  "max_year": 2100
}

min_year is the earliest art movement start; max_year is the latest of era ends, movement ends, and artist death years.


GET /api/timeline

Historical eras and art movements overlapping a year window.

Query

Param Type Default Description
start int -3000 Window start year
end int 2100 Window end year

Response

{
  "eras": [ { "id": 1, "name": "Renaissance", "start_year": 1300, "end_year": 1600, "start_definite": false, "end_definite": false, "description": "...", "sort_order": 3 } ],
  "movements": [ { "id": 5, "name": "Impressionism", "start_year": 1860, "end_year": 1890, "era_id": 6, "era_name": "Modern", "color": "#87CEEB", ... } ]
}

Movements are filtered to those with at least one artist active in the requested range.


GET /api/artists

Artists for timeline portraits and the movement flow diagram.

Query

Param Type Description
start int Only artists alive after this year
end int Only artists born before this year
movement_id int Filter by movement

Response — array of artist objects with joined movement_name and movement_color.


GET /api/movements/:id/artists

Artists belonging to a single movement.

Response — array of { id, name, birth_year, death_year, portrait_path, bio_short }.


GET /api/artists/:id

Full artist profile for the bio page and 3D gallery entry.

Response

{
  "artist": {
    "id": 1,
    "name": "...",
    "birth_year": 1840,
    "death_year": 1926,
    "movement_name": "...",
    "portrait_path": "portraits/Claude_Monet.jpg",
    "bio_short": "First two sentences from Wikipedia…",
    "bio_full": "Full Wikipedia lead section…",
    "wikipedia_title": "Claude Monet"
  },
  "periods": [ { "id": 1, "name": "Milan Period", "start_year": 1482, "end_year": 1499, ... } ],
  "paintings": [ { "id": 10, "title": "...", "year": 1498, "image_path": "...", "thumbnail_path": "...", "wikipedia_title": "...", "has_influence_links": true, ... } ]
}

Each painting includes has_influence_links (boolean) — true when the work appears in any painting_influences row as source or target. The 3D gallery uses this to show a golden lamp above the frame.

Populate biographies with npm run fetch-artist-bios (see data-and-images.md).


GET /api/artists/:id/navigation

Related artists for hall-to-hall navigation at the exit doorway. Derived from painting_influences:

  • Predecessors — artists whose works influenced this artists paintings.
  • Successors — artists whose works were influenced by this artists paintings.

Both lists are grouped by art movement and exclude the current artist.

Response

{
  "predecessors": [
    {
      "movement_id": 4,
      "movement_name": "Early Renaissance",
      "movement_color": "#B8860B",
      "artists": [
        {
          "id": 12,
          "name": "Domenico Ghirlandaio",
          "birth_year": 1448,
          "death_year": 1494,
          "portrait_path": "portraits/Domenico_Ghirlandaio.jpg"
        }
      ]
    }
  ],
  "successors": []
}

POST /api/artists/:id/preload-images

Fast local scan: links paintings to files already on disk. Does not download from the internet (safe to call before opening the 3D gallery).

Response

{ "fetched": 12, "total": 15 }

fetched counts paintings with a resolvable local image after the scan.


GET /api/paintings/:id

Painting detail with influence graph neighbours.

Response

{
  "painting": { "id": 10, "title": "...", "artist_name": "...", "image_path": "...", "has_influence_links": true, ... },
  "influencedBy": [
    {
      "source_type": "painting",
      "id": 3, "title": "...", "artist_name": "...", "year": 1885,
      "notes": "...", "aspects": "...", "quote": "...", "source_author": "...", "source_url": "..."
    },
    {
      "source_type": "artist",
      "source_artist_id": 12, "source_artist_name": "Paul Cézanne",
      "artist_portrait": "...", "period_note": "during creation (1907)",
      "period_start_year": 1907, "period_end_year": 1907
    },
    {
      "source_type": "movement",
      "movement_id": 8, "movement_name": "Fauvism", "movement_color": "#c45c26",
      "period_start_year": 1905, "period_end_year": 1907
    }
  ],
  "influenced": [ { "source_type": "painting", "id": 20, "title": "...", ... } ]
}

painting.has_influence_links matches the flag on GET /api/artists/:id paintings (see above).


GET /api/paintings/:id/image

Serves a painting image file. Resolves and caches from external sources only when no local file exists.

Query

Param Values Default
size thumb, full thumb

Returns the image bytes with Cache-Control: public, max-age=86400, or 404 if unavailable.


Frontend helpers

The React client wraps these endpoints in client/src/api/client.ts:

Function Maps to
api.getBounds() GET /api/bounds
api.getTimeline(start, end) GET /api/timeline
api.getArtists(...) GET /api/artists
api.getArtist(id) GET /api/artists/:id
api.getArtistNavigation(id) GET /api/artists/:id/navigation
api.getPainting(id) GET /api/paintings/:id
preloadArtistImages(id) POST /api/artists/:id/preload-images
imageUrl(path) /images/<path> or placeholder
galleryImageUrl(painting) Local thumb/full only (3D)
paintingImageUrl(painting) Local file or on-demand API