Skip to content

feat(api): add slim series external-index endpoint for discovery tools#27

Merged
AshDevFr merged 3 commits into
mainfrom
tsundoku-support
May 30, 2026
Merged

feat(api): add slim series external-index endpoint for discovery tools#27
AshDevFr merged 3 commits into
mainfrom
tsundoku-support

Conversation

@AshDevFr
Copy link
Copy Markdown
Owner

Summary

Adds a read-only, paginated GET /api/v1/series/external-index endpoint that projects each series down to just the fields an external discovery tool needs: the series ID, its linked external IDs, and how far the local library has progressed. It is a lightweight alternative to fetching the full series payload purely to read external IDs.

Motivation

There was no way to enumerate series alongside their external IDs in a single slim call. The full series response returns external IDs but carries metadata, genres, tags, covers, ratings, and links that a discovery consumer would fetch and immediately discard, while the lightweight series response omits external IDs entirely. As a result, a tool that just wants "what does this library own, keyed by external ID" had to over-fetch the full payload for every series.

Changes

  • API: New GET /api/v1/series/external-index?page=&pageSize= endpoint returning a paginated list of series, each with id, externalIds (source, externalId, optional externalUrl), localMaxVolume, localMaxChapter, and volumesOwned. Items are returned under the standard paginated data key.
  • API: Series with no external IDs are still included (with an empty externalIds array) so consumers can offer manual linking.
  • API: Requires the SeriesRead permission (Reader role and up); anonymous requests are rejected. Results honor the same sharing-tag content visibility rules as the main series listing, so callers only see series they can access.
  • Docs / types: OpenAPI spec and generated TypeScript types updated to include the new endpoint and its response schemas.

Notes

  • volumesOwned is an approximate file count, not an authoritative measure; the reliable "how far along" signals are localMaxVolume and localMaxChapter.
  • There is no incremental/delta sync (e.g. updatedSince); consumers re-sweep the full list, which is fine at personal-library scale. pageSize is capped at the existing maximum.

AshDevFr added 3 commits May 29, 2026 16:27
Add GET /api/v1/series/external-index returning a paginated, slim
per-series projection: series UUID, linked external IDs (source,
externalId, optional externalUrl), local volume/chapter maxima, and
volumes owned. It is a lightweight alternative to
GET /api/v1/series?full=true for external discovery consumers that key
on external IDs and would otherwise over-fetch and discard the full DTO.

Gated by SeriesRead and honors sharing-tag content visibility, so it
only exports series the caller can access; never anonymous. Reuses the
existing batch repositories (get_for_series_ids and
get_book_classification_aggregates_for_series_ids) over just the current
page's IDs, with no new SQL or schema. Ordering is fixed to name-asc for
deterministic single-sweep pagination.

Items are wrapped under the standard PaginatedResponse `data` key,
consistent with the other v1 list endpoints. Registers the new schemas
and path in the OpenAPI document. Adds handler tests covering response
shape, a series with no external IDs, classification counts, pagination
total, and the anonymous 401 path.
…endpoint

Regenerate the OpenAPI specification and frontend TypeScript types to
reflect the series external-index endpoint added to the backend.

Adds the /api/v1/series/external-index path and the SeriesExternalIndexDto
and SeriesExternalIdRefDto schemas to web/openapi.json and the mirrored
docs/api/openapi.json, with matching types in the generated TypeScript
client. The endpoint's paginated response reuses the shared
PaginatedResponse schema, consistent with the other series list endpoints.

Generated via `make openapi-all`; the pre-commit OpenAPI sync check
passes with no drift.
The openapi-sync pre-commit hook still matched the pre-crate paths
(src/api/*.rs, src/db/entities/*.rs), which no longer exist after the
workspace was split into crates. As a result the hook never fired on
backend changes, so OpenAPI spec/type drift could slip through.

Point the files pattern at the crates that actually feed the generated
spec via utoipa annotations: codex-api, codex-models, codex-services,
codex-tasks, codex-events, and codex-db.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying codex with  Cloudflare Pages  Cloudflare Pages

Latest commit: 81c57ff
Status: ✅  Deploy successful!
Preview URL: https://b68fbccc.codex-asm.pages.dev
Branch Preview URL: https://tsundoku-support.codex-asm.pages.dev

View logs

@AshDevFr AshDevFr merged commit 4311c5c into main May 30, 2026
19 checks passed
@AshDevFr AshDevFr deleted the tsundoku-support branch May 30, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant