From 1c5a449d845f60288a13cff68bd8a6ba9e2ff761 Mon Sep 17 00:00:00 2001 From: khannurien Date: Fri, 9 Oct 2026 22:25:08 +0000 Subject: [PATCH] =?UTF-8?q?v4:=20API.md=20says=20what=20the=20URLs=20are?= =?UTF-8?q?=20again=20=E2=80=94=20it=20lacked=20the=20/playlists=20pages?= =?UTF-8?q?=20and=20GET=20/api/playlists,=20a=20playlist's=20categories,?= =?UTF-8?q?=20the=20=3Fcategory=3D=20of=20a=20user's=20lists=20and=20of=20?= =?UTF-8?q?the=20search,=20and=20=3Fchat=3D,=20and=20its=20r?= =?UTF-8?q?eserved=20words=20lacked=20playlists.=20It=20now=20also=20state?= =?UTF-8?q?s=20the=20rules=20the=20pages=20kept=20without=20saying:=20a=20?= =?UTF-8?q?category=20is=20a=20segment=20where=20the=20page=20is=20the=20c?= =?UTF-8?q?ategory's=20own=20and=20=3Fcategory=3D=20where=20it=20narrows?= =?UTF-8?q?=20a=20list=20on=20another=20page;=20switching=20tabs=20takes?= =?UTF-8?q?=20the=20place=20of=20the=20history=20entry,=20while=20picking?= =?UTF-8?q?=20a=20category=20or=20opening=20another=20page=20or=20an=20edi?= =?UTF-8?q?tor=20adds=20one;=20a=20signed-in=20page=20opened=20signed=20ou?= =?UTF-8?q?t=20comes=20back=20after=20/login.=20canonical-url.ts=20named?= =?UTF-8?q?=20GET=20/api/dumps/by-url,=20which=20is=20GET=20/api/dumps=3Fu?= =?UTF-8?q?rl=3D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- API.md | 40 +++++++++++++++++++++++++++------------- api/lib/canonical-url.ts | 6 +++--- shared/paths.ts | 7 ++++--- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/API.md b/API.md index 486f22fa..9dd015eb 100644 --- a/API.md +++ b/API.md @@ -11,7 +11,13 @@ paths. whose account lives on another instance. Any other word under the root is a category or an app page. Under a user, each segment is a fixed tab name, and under `dumps` or `playlists`, a slug. A view of a page is one more segment. A -default view is the bare path, with no redirect. +default view is the bare path, with no redirect. A category is a segment where +the page is the category's own, a dump category's feed at the root (`/music`) +and a playlist category's under `/playlists` (`/playlists/mixes`); where it +narrows a list on another page, a profile's or a search's, it is `?category=`, +of the kind that list holds. Switching tabs, or a tab's views, takes the place +of the current history entry; picking a category, or opening another page or an +editor, adds one. **API.** A resource is `/api//[/]`. Its key is its id, except a user's, which may also be `@name`. The `@` keeps the two apart, so no regex @@ -27,14 +33,15 @@ has one word, the same as in pages. | `/` | `/hot`, `/new`, `/journal`, `/follow`, `/follow/playlists`; the bare `/` is the viewer's default tab, the bare `/follow` the users followed | anyone, `/follow` signed in | `Index` | | `/` | `//hot`, `/new`, `/journal`, `/follow`, `/follow/playlists` | anyone, `/follow` signed in | `Index` | | `/@` | the overview: dumps beside upvoted | anyone | `UserPublicProfile` | -| `/@/dumps`, `/upvoted`, `/playlists`, `/invitees` | | anyone | `UserPublicProfile` | +| `/@/dumps`, `/upvoted`, `/playlists`, `/invitees` | `?category=` narrows the dumps, the upvoted or the playlists | anyone | `UserPublicProfile` | | `/@/follow` | `/@/follow/playlists`; the bare tab lists users | anyone | `UserPublicProfile` | | `/@/manage` | | `user:manage`, on others' profiles | `UserPublicProfile` | | `/@/dumps/` | | anyone; a private dump, its poster | `Dump` | | `/@/dumps//edit` | | its poster, `dump:moderate` | `DumpEdit` | | `/@/playlists/` | | anyone; a private playlist, its owner | `PlaylistDetail` | | `/@/playlists//edit` | | its owner, `playlist:moderate` | `PlaylistDetail` | -| `/search?q=` | `/search/users?q=`, `/search/playlists?q=` | anyone | `Search` | +| `/playlists` | `/playlists/`, a playlist category's | anyone | `Playlists` | +| `/search?q=` | `/search/users?q=`, `/search/playlists?q=`; `?category=` narrows the dumps or the playlists | anyone | `Search` | | `/notifications` | | signed in | `Notifications` | | `/community` | `/community/monthly`; the bare path is the week | signed in | `Community` | | `/settings` | | signed in | `Settings` | @@ -45,9 +52,10 @@ has one word, the same as in pages. | `/reset-password` | `/reset-password?token=`, from the emailed link, sets the new password | anyone | `ResetPassword` | | `/unsubscribe?token=` | | anyone | `Unsubscribe` | -`/?share_url=` opens the create form on a link shared from Android, -`/?chat=open` opens the chat over any page, and `#comment-` scrolls a dump's -page to a comment. +`/?share_url=` opens the create form on a link shared from Android, `?chat=open` +opens the chat over any page, `?chat=` on that message, and +`#comment-` scrolls a dump's page to a comment. A signed-in page opened +signed out replaces itself with `/login?next=`. ## Names @@ -78,8 +86,8 @@ page to a comment. answers the same path with a 301 to crawlers and full page loads. - **Reserved words.** A category can't take a word the root already uses: `hot new journal follow search login register notifications reset-password - unsubscribe settings manage community authorize federation api ws assets - emojibase` + unsubscribe settings manage community playlists authorize federation api ws + assets emojibase` (`api/lib/reserved-slugs.ts`). Dump and playlist slugs reserve nothing, and no slug has an `@`. @@ -126,7 +134,8 @@ there: those endpoints answer it 400. - `GET /api/users/:key` — anyone. A profile. - `GET /api/users/:key/avatar` — anyone. The avatar. - `GET /api/users/:key/dumps`, `/upvoted`, `/playlists` — anyone; private items - to their owner only. Their lists. + to their owner only. Their lists; `?category=` narrows one to a category of + its kind. - `GET /api/users/:key/dumps/:slug`, `/playlists/:slug` — anyone; private items to their owner only. An item by its page path, by an alias too. - `GET /api/users/:key/follow/users`, `/follow/playlists` — anyone. What they @@ -163,10 +172,13 @@ there: those endpoints answer it 400. ### playlists -- `POST /api/playlists` — signed in. Creates a playlist. +- `POST /api/playlists` — signed in. Creates a playlist, filed under the + playlist categories it names. +- `GET /api/playlists` — anyone. Every public playlist, newest first; + `?category=` narrows it to a playlist category. - `GET /api/playlists/:id` — anyone who may see it. A playlist and its dumps. - `PUT`, `DELETE /api/playlists/:id` — its owner, `playlist:moderate`. Title, - slug, description, visibility; deletion. + slug, description, visibility, categories; deletion. - `POST`, `DELETE /api/playlists/:id/dumps/:dumpId` — the same. Adds or removes a dump. - `PUT /api/playlists/:id/order` — the same. Reorders its dumps. @@ -181,7 +193,8 @@ there: those endpoints answer it 400. ### categories -- `GET /api/categories` — anyone. Every category. +- `GET /api/categories` — anyone. Every category, dump and playlist ones apart: + each has a `kind`, and its slug is unique within its kind. - `POST /api/categories`, `PUT`, `DELETE /api/categories/:id` — `category:manage`. Changes them. @@ -249,7 +262,8 @@ name those pages back as where its `/authorize` is. ### search - `GET /api/search?q=` — anyone. A page of dumps, and up to 20 users and - playlists. + playlists. It finds both kinds of item at once, so `?category=` narrows the + dumps and `?playlistCategory=` the playlists. ### attachments diff --git a/api/lib/canonical-url.ts b/api/lib/canonical-url.ts index 00b567c2..84468684 100644 --- a/api/lib/canonical-url.ts +++ b/api/lib/canonical-url.ts @@ -1,9 +1,9 @@ /** * Spots reposts and links to known dumps, by reducing a URL to a key shared by * every link to the same page or video. `dump-service` stores it in - * `dumps.url_canonical` when a dump is posted or edited, `GET - * /api/dumps/by-url` matches on it to warn the create form of a repost, and - * backlink-service compares links by it. The key is never fetched or shown, so + * `dumps.url_canonical` when a dump is posted or edited, `GET /api/dumps?url=` + * matches on it to warn the create form of a repost, and backlink-service + * compares links by it. The key is never fetched or shown, so * it may be lossy: it forces `https`, drops `www.`, tracking parameters and * timestamps. * diff --git a/shared/paths.ts b/shared/paths.ts index 8ec620d3..9d8d1f19 100644 --- a/shared/paths.ts +++ b/shared/paths.ts @@ -3,9 +3,10 @@ * the path is a hierarchy, `@name` is a user (`@name@host` one whose account * lives on another instance), and any other word under the * root is a dump category or an app page, `/playlists` holding the playlist - * categories. Under a user each segment is a fixed tab - * name, and under `dumps` or `playlists` a slug. A view of a page is one more - * segment, and a default view is the bare path. + * categories. Under a user each segment is a fixed tab name, and under `dumps` + * or `playlists` a slug. A view of a page is one more segment, and a default + * view is the bare path. A category narrowing a list on another page, a + * profile's or a search's, is a query, `?category=`, not a segment. * * The client links every page through the builders (`src/utils/shared/urls.ts` * wraps the dump and playlist ones), the emails prefix them with the