Add project documentation under Documentation/.

Covers architecture, setup, database schema, API reference, and image pipeline; link from README.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-06-19 09:46:44 +03:00
co-authored by Cursor
parent 40db99d734
commit 44d3d4359a
6 changed files with 577 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
# 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**
```json
{
"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**
```json
{
"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 movement bands.
**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**
```json
{
"artist": { "id": 1, "name": "...", "movement_name": "...", "bio_full": "...", ... },
"periods": [ { "id": 1, "name": "Milan Period", "start_year": 1482, "end_year": 1499, ... } ],
"paintings": [ { "id": 10, "title": "...", "year": 1498, "image_path": "...", "thumbnail_path": "...", ... } ]
}
```
---
## `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**
```json
{ "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**
```json
{
"painting": { "id": 10, "title": "...", "artist_name": "...", "image_path": "...", ... },
"influencedBy": [ { "id": 3, "title": "...", "artist_name": "...", "notes": "...", "aspects": "...", "quote": "...", "source_author": "...", ... } ],
"influenced": [ { "id": 20, "title": "...", ... } ]
}
```
---
## `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.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 |