Initial Aurora Core 0.1 implementation
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
Production-grade media metadata backend per docs/aurora-core-spec.md: Aurora-owned UUIDv7 identity system, normalized movie/TV/people/credits schema, provider abstraction with a TMDb adapter, an idempotent async ingestion pipeline (normalize -> match -> upsert -> provenance), a Postgres FTS/trigram search layer, a stateless FastAPI v1 API with Redis caching/rate-limiting, arq workers, dev/prod Docker Compose topologies, unit + integration + load tests, CI, and architecture/ database/API/ERD documentation. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
# Aurora Core — API
|
||||
|
||||
Base path: `/api/v1`. Interactive OpenAPI docs are available at `/docs` in
|
||||
any environment where `AURORA_API_DOCS_ENABLED=true` (default; set to
|
||||
`false` to hide docs in production — section 32).
|
||||
|
||||
All responses are JSON, built from Pydantic response schemas
|
||||
(`app/schemas/`), never from serialized ORM objects (section 18). Errors use
|
||||
a consistent envelope:
|
||||
|
||||
```json
|
||||
{ "error": { "code": "movie_not_found", "message": "No such movie: aur_movie_..." } }
|
||||
```
|
||||
|
||||
## Health
|
||||
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| GET | `/health` and `/api/v1/health` | Liveness only. |
|
||||
| GET | `/ready` and `/api/v1/ready` | Checks PostgreSQL (always) and Redis (if `REDIS_REQUIRED=true`). |
|
||||
|
||||
## Search
|
||||
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| GET | `/api/v1/search?q=...&limit=10` | Searches movies, series, people, collections, companies. `limit` (1-50) caps results **per type**. |
|
||||
|
||||
```json
|
||||
GET /api/v1/search?q=the%20matrix
|
||||
|
||||
{
|
||||
"query": "the matrix",
|
||||
"movies": [ { "id": "aur_movie_...", "type": "movie", "title": "The Matrix", "release_date": "1999-03-30", "poster_path": "https://..." } ],
|
||||
"series": [],
|
||||
"people": [],
|
||||
"collections": [ { "id": "aur_collection_...", "name": "The Matrix Collection" } ],
|
||||
"companies": [],
|
||||
"total_results": 2
|
||||
}
|
||||
```
|
||||
|
||||
## Movies
|
||||
|
||||
| Method | Path |
|
||||
|---|---|
|
||||
| GET | `/api/v1/movies/{aurora_id}` |
|
||||
| GET | `/api/v1/movies/{aurora_id}/credits` |
|
||||
| GET | `/api/v1/movies/{aurora_id}/images` |
|
||||
| GET | `/api/v1/movies/{aurora_id}/videos` |
|
||||
|
||||
`GET /api/v1/movies/{aurora_id}` (cached, see `docs/architecture.md#caching`):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "aur_movie_01m1q7p1c...",
|
||||
"type": "movie",
|
||||
"title": "The Matrix",
|
||||
"original_title": "The Matrix",
|
||||
"overview": "...",
|
||||
"tagline": "Welcome to the Real World",
|
||||
"status": "released",
|
||||
"original_language": "en",
|
||||
"release_date": "1999-03-30",
|
||||
"runtime": 136,
|
||||
"budget": 63000000,
|
||||
"revenue": 463517383,
|
||||
"popularity": 45.5,
|
||||
"vote_average": 8.2,
|
||||
"vote_count": 24000,
|
||||
"adult": false,
|
||||
"genres": [{ "id": "aur_genre_...", "name": "Action" }],
|
||||
"keywords": [{ "id": "aur_keyword_...", "name": "dystopia" }],
|
||||
"cast": [{ "id": "aur_credit_...", "person": { "id": "aur_person_...", "name": "Keanu Reeves", "profile_path": null }, "character": "Neo", "billing_order": 0 }],
|
||||
"crew": [{ "id": "aur_credit_...", "person": { "id": "aur_person_...", "name": "Lana Wachowski" }, "department": "Directing", "job": "Director" }],
|
||||
"companies": [{ "id": "aur_company_...", "name": "Village Roadshow Pictures", "company_type": "production_company" }],
|
||||
"collection": { "id": "aur_collection_...", "name": "The Matrix Collection" },
|
||||
"images": { "posters": [...], "backdrops": [...], "logos": [...] },
|
||||
"videos": [{ "id": "aur_vid_...", "type": "trailer", "site": "YouTube", "url": "https://www.youtube.com/watch?v=...", "is_official": true }],
|
||||
"external_ids": { "tmdb": "603", "imdb": "tt0133093" },
|
||||
"created_at": "...",
|
||||
"updated_at": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## Series / Seasons / Episodes
|
||||
|
||||
| Method | Path |
|
||||
|---|---|
|
||||
| GET | `/api/v1/series/{aurora_id}` |
|
||||
| GET | `/api/v1/series/{aurora_id}/seasons` |
|
||||
| GET | `/api/v1/seasons/{aurora_id}` |
|
||||
| GET | `/api/v1/episodes/{aurora_id}` |
|
||||
|
||||
Series detail includes `networks` and `companies` as separate lists (a TV
|
||||
network and a production company are both `Company` rows distinguished by
|
||||
`company_type` — see `docs/database.md`), plus `creators` (series-level
|
||||
"Creator" credits) alongside `cast`/`crew`.
|
||||
|
||||
## People
|
||||
|
||||
| Method | Path |
|
||||
|---|---|
|
||||
| GET | `/api/v1/people/{aurora_id}` |
|
||||
| GET | `/api/v1/people/{aurora_id}/credits` (movie filmography in 0.1; series/episode credits are stored but not yet surfaced here — see Known limitations) |
|
||||
|
||||
## Collections / Companies
|
||||
|
||||
| Method | Path |
|
||||
|---|---|
|
||||
| GET | `/api/v1/collections/{aurora_id}` |
|
||||
| GET | `/api/v1/companies/{aurora_id}` |
|
||||
|
||||
## Administrative (section 17: "separately protected")
|
||||
|
||||
Require header `X-API-Key: <one of AURORA_ADMIN_API_KEYS>`. Never exposed to
|
||||
public clients — mount behind a separate Traefik router/ACL in production if
|
||||
you want network-level isolation too.
|
||||
|
||||
| Method | Path | Body | Notes |
|
||||
|---|---|---|---|
|
||||
| POST | `/api/v1/admin/ingest/movie` | `{"tmdb_id": "603"}` | Enqueues an arq job; returns `{"job_id": "...", "status": "enqueued"}` immediately (section 15: ingestion never runs inline in a request). |
|
||||
| POST | `/api/v1/admin/ingest/person` | `{"tmdb_id": "6384"}` | Same, for a person profile. |
|
||||
| GET | `/api/v1/admin/ingest/jobs/{job_id}` | — | Poll job status: `pending` → `running` → `succeeded`/`failed`/`retrying`. |
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | When |
|
||||
|---|---|
|
||||
| 404 | Entity not found, or the ID is well-formed but the wrong entity type (`aur_person_...` passed to `/movies/{id}`) |
|
||||
| 422 | Request validation failure (e.g. missing/invalid query params) |
|
||||
| 429 | Rate limit exceeded (`Retry-After` header set) |
|
||||
| 401 | Missing/invalid `X-API-Key` on an admin endpoint |
|
||||
| 503 | Admin API called with `AURORA_ADMIN_API_KEYS` unset, or the job queue is unavailable |
|
||||
|
||||
## Known limitations
|
||||
|
||||
- No pagination on list-shaped responses yet beyond search's `limit`
|
||||
(movie/series lists like recommendations return everything). Cursor-based
|
||||
pagination using the Aurora ID's inherent creation-order sort (see
|
||||
`docs/database.md#aurora-ids-vs-primary-keys`) is the natural next step.
|
||||
- `/people/{id}/credits` only returns movie credits today; series/episode
|
||||
credits exist in the `credits` table but the response assembly for them
|
||||
is not yet wired up (`app/services/person_service.py`).
|
||||
Reference in New Issue
Block a user