Collections View
The cross-owner MediaStore.list — a cursor-paginated, filterable listing of media-library records across every owner (newest first), for management and console reads. Backs the dashboard's Collections view.
listByOwner answers the question the library itself asks all day: what has this entity got? Management and console views need the opposite question — every record across all owners, paginated and filterable, newest first. That is MediaStore.list, and it is what the Dashboard's Collections view is built on.
list is a read API for consoles and admin tooling. It's implemented by both bundled stores (in-memory + Lucid). Your own custom store must implement it too — it's part of the MediaStore interface.
MediaStore.list
Reach the store through media.store (exposed on the manager for exactly these reads; day-to-day writes still go through media.library):
import media from '@adonis-agora/media/services/main'
const page = await media.store.list({
collection: 'gallery', // optional filters — all AND-ed
ownerType: 'Post',
ownerId: '42',
prefix: 'Post/42/', // records whose `path` starts with this
limit: 50, // default 50, clamped to 200
})
page.items // MediaRecord[] — ordered createdAt desc, then id desc
page.nextCursor // opaque string, or null on the last page
// next page:
const more = await media.store.list({ cursor: page.nextCursor ?? undefined })| Field | Type | Notes |
|---|---|---|
collection | string? | Restrict to one collection. |
ownerType | string? | Restrict to one owner type. |
ownerId | string? | Restrict to one owner id (usually paired with ownerType). |
prefix | string? | Records whose path starts with this prefix. |
cursor | string? | Opaque cursor from a previous page's nextCursor. |
limit | number? | Page size. Defaults to DEFAULT_MEDIA_LIST_LIMIT (50), clamped to MAX_MEDIA_LIST_LIMIT (200). |
Ordering is a stable keyset — createdAt desc, then id desc — so paging stays consistent under concurrent writes (no rows skipped or repeated when new media is attached mid-scan). The cursor carries the last row's { createdAt, id }, URL-safe base64 encoded.
The helpers encodeMediaCursor / decodeMediaCursor / clampMediaListLimit and the DEFAULT_MEDIA_LIST_LIMIT / MAX_MEDIA_LIST_LIMIT constants are exported from the barrel if you build your own store or paginate manually.
The dashboard Collections view
The console's GET /collections route projects each MediaRecord into a MediaEntry (id, owner, collection, name, file name, MIME type, size, disk, path, the names of generated conversions, and timestamps) and returns a CollectionListResponse — { items, nextCursor, prevCursor, hasNext, hasPrev } — driven directly by media.store.list. The same filters (collection / ownerType / ownerId / prefix) are exposed as query parameters, so you can drill into one collection or one owner from the UI.
Two vocabularies, one mechanism
The route paginates with ?after=&first= and returns the ecosystem's cursor page (see
Cursor pagination) — the shape every @adonis-agora/*
library shares. The store SPI underneath it keeps its own { cursor, limit } → { items, nextCursor } vocabulary: it is a persistence contract that your custom store implements, not a
listing API a client calls, and every bundled and third-party store already speaks it. The route
maps after → cursor and first → limit; prevCursor / hasPrev are always null /
false, because the keyset walks forward only.
Two more routes hang off the same view. GET /collections/summary returns a per-collection rollup — record count and total bytes — which the UI renders as one-click filter chips; it walks list() in pages and sums client-side, bounded so a very large library degrades to a partial summary rather than an unbounded scan. And GET /media-record?id drills into a single row, returning the record plus a signed URL per conversion (only conversions that actually wrote an artifact get one — a metadata-only probe result has nothing to link to). With actions: true, POST /media-record/delete removes the row; note that it deletes the record, not the object on disk.
The listing itself is read-only: it shows what the library has recorded, independent of the raw bucket-browsing Library view (which lists disk keys). Together they let you see both the storage layer and the media-library metadata over it.
Next steps
- Dashboard — the console that surfaces this view
- Programmatic API —
DashboardService.collections/collectionsSummary/mediaRecord - Stores & Processors — the
MediaStoreinterfacelistbelongs to
Console Authentication
The built-in session-cookie login for the media console — Mode A mints a session from your app's own auth, Mode B accepts credentials at a login screen. Plus the exported signing helpers and how the guard slides a live session forward.
Programmatic API
DashboardService, DashboardError, MediaManagerLike and the JSON contract types — the console's logic as a plain, framework-free object you can call from your own routes, plus the response shapes shared by server and SPA.