210 lines
9.7 KiB
Markdown
210 lines
9.7 KiB
Markdown
# gerbeur's URLs
|
|
|
|
Every page and every API endpoint follows one of two rules. This file
|
|
summarizes them. Each router's module doc in `api/routes/` lists its own
|
|
endpoints in full, and a route change updates both. `shared/paths.ts` builds
|
|
and parses the page paths.
|
|
|
|
## The two rules
|
|
|
|
**Pages.** The path is a hierarchy. `@name` is a user. 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.
|
|
|
|
**API.** A resource is `/api/<plural>/<key>[/<noun>]`. Its key is its id,
|
|
except a user's, which may also be `@name`. The `@` keeps the two apart, so no
|
|
regex sniffing is needed. A read by page path mirrors the page:
|
|
`/api/users/@bob/dumps/<slug>`. The caller's own things live under `/api/me`.
|
|
Media is a sub-resource. A lookup by another key is a query filter. Each
|
|
concept has one word, the same as in pages.
|
|
|
|
## Pages
|
|
|
|
| Path | Views | Access | Page |
|
|
|---|---|---|---|
|
|
| `/` | `/hot`, `/new`, `/journal`, `/follow`; the bare `/` is the viewer's default tab | anyone, `/follow` signed in | `Index` |
|
|
| `/<category>` | `/<category>/hot`, `/new`, `/journal`, `/follow` | anyone | `Index` |
|
|
| `/@<name>` | the overview: dumps beside upvoted | anyone | `UserPublicProfile` |
|
|
| `/@<name>/dumps`, `/upvoted`, `/playlists`, `/invitees` | | anyone | `UserPublicProfile` |
|
|
| `/@<name>/follow` | `/@<name>/follow/playlists`; the bare tab lists users | anyone | `UserPublicProfile` |
|
|
| `/@<name>/manage` | | `user:manage`, on others' profiles | `UserPublicProfile` |
|
|
| `/@<name>/dumps/<slug>` | | anyone; a private dump, its poster | `Dump` |
|
|
| `/@<name>/dumps/<slug>/edit` | | its poster, `dump:moderate` | `DumpEdit` |
|
|
| `/@<name>/playlists/<slug>` | | anyone; a private playlist, its owner | `PlaylistDetail` |
|
|
| `/search?q=` | `/search/users?q=`, `/search/playlists?q=` | anyone | `Search` |
|
|
| `/notifications` | | signed in | `Notifications` |
|
|
| `/community` | `/community/monthly`; the bare path is the week | signed in | `Community` |
|
|
| `/settings` | | signed in | `Settings` |
|
|
| `/manage` | | `category:manage` | `Manage` |
|
|
| `/login`, `/register?token=` | | signed out | `UserLogin`, `UserRegister` |
|
|
| `/reset-password?token=`, `/unsubscribe?token=` | | anyone | `ResetPassword`, `Unsubscribe` |
|
|
|
|
`/?share_url=` opens the create form on a link shared from Android,
|
|
`/?chat=open` opens the chat over any page, and `#comment-<id>` scrolls a dump's
|
|
page to a comment.
|
|
|
|
## Names
|
|
|
|
- **Slugs** of categories, dumps and playlists are lowercase letters and
|
|
digits in words joined by single hyphens (`shared/slug.ts`). A category's is
|
|
at most 50 characters, a dump's or a playlist's at most 60.
|
|
- **A dump's or a playlist's slug** is made from its title when it is created,
|
|
unique among its owner's items of that kind: a clash gets `-2`, `-3`. It
|
|
changes only when someone edits it: the edit forms propose
|
|
`slugify(newTitle)` when the title changes. Its owner may change it 3 times
|
|
a day; moderators (`dump:moderate`, `playlist:moderate`) may edit any slug,
|
|
at any time.
|
|
- **A username** is 1 to 32 letters, digits and underscores, unique in any
|
|
case. A page writes it in its stored case, and another case still resolves.
|
|
Users can't rename themselves: those with `user:manage` rename anyone, from
|
|
`/@<name>/manage`.
|
|
- **Aliases.** A slug or a name left behind stays an alias for good, which
|
|
always names the item or user itself, so redirects never chain. Nobody else
|
|
can take one. An item can take back one of its own. Deleting an item frees
|
|
its aliases.
|
|
- **Redirects.** A page reached by an alias, or by a name in another case,
|
|
gets the item from the API under its current path and replaces its URL.
|
|
The server 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 api ws assets emojibase`
|
|
(`api/lib/reserved-slugs.ts`). Dump and playlist slugs reserve nothing, and
|
|
no slug has an `@`.
|
|
|
|
## API
|
|
|
|
Every JSON endpoint answers the envelope of `shared/api.ts`:
|
|
`{ "success": true, "data": … }`, or `{ "success": false, "error": { "code",
|
|
"message" } }` with the matching HTTP status. A list answers
|
|
`{ "items", "hasMore" }` and takes `?page=` (from 1) and `?limit=` (20 by
|
|
default, 100 at most). "Signed in" means a session token as
|
|
`Authorization: Bearer …`. Media can't send a header, so a private file,
|
|
thumbnail, cover or attachment takes the owner's media token as `?token=`.
|
|
|
|
### me
|
|
|
|
- `GET /api/me` — signed in. The caller's own user.
|
|
- `PATCH /api/me` — signed in. Changes the description and email.
|
|
- `GET /api/me/media-token` — signed in. A media token.
|
|
- `GET`, `PATCH /api/me/settings` — signed in. The caller's settings.
|
|
- `PUT /api/me/password` — signed in, with the current one. A new password,
|
|
and fresh tokens.
|
|
- `PUT /api/me/avatar` — signed in. A new avatar (multipart).
|
|
- `GET /api/me/playlists?contains=:dumpId` — signed in. The caller's
|
|
playlists, and whether each holds the dump.
|
|
- `GET /api/me/follow` — signed in. The ids of the users and playlists
|
|
followed.
|
|
- `PUT`, `DELETE /api/me/follow/users/:id`, `/api/me/follow/playlists/:id` —
|
|
signed in. Follows or stops following.
|
|
- `GET /api/me/feed?from=users|playlists&category=` — signed in. The dumps of
|
|
what the caller follows.
|
|
- `GET /api/me/notifications` — signed in. The caller's notifications.
|
|
- `POST /api/me/notifications/read` — signed in. Marks them all read.
|
|
|
|
### users
|
|
|
|
- `POST /api/users` — with an invite. Signs up.
|
|
- `GET /api/users?q=` — anyone. Up to 8 users whose name starts with `q`.
|
|
- `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.
|
|
- `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
|
|
follow.
|
|
- `GET /api/users/:key/invitees` — anyone. The tree of users they invited.
|
|
- `PATCH /api/users/:key/role`, `/username` — `user:manage`. Another user's
|
|
role, or anyone's name, theirs included.
|
|
|
|
### dumps
|
|
|
|
- `POST /api/dumps` — signed in. Posts a link (JSON) or a file (multipart).
|
|
- `GET /api/dumps` — anyone. The feed; `?category=<slug>` narrows it, and
|
|
`?url=` answers the dumps of the same link instead.
|
|
- `GET /api/dumps/:id` — anyone who may see it. A dump.
|
|
- `GET /api/dumps/:id/voters`, `/related`, `/playlists` — the same. Who
|
|
upvoted it, the dumps linked to or from it, the playlists holding it.
|
|
- `PUT /api/dumps/:id` — its poster, `dump:moderate`. Title, slug, note,
|
|
visibility, categories, link.
|
|
- `POST /api/dumps/:id/metadata` — the same. Fetches a link's preview again.
|
|
- `DELETE /api/dumps/:id` — the same. Deletes it.
|
|
- `GET`, `PUT /api/dumps/:id/file` — the file; a new one, by its poster or
|
|
`dump:moderate`.
|
|
- `GET`, `PUT`, `DELETE /api/dumps/:id/thumbnail` — the thumbnail; a custom
|
|
one, set or removed by its poster or `dump:moderate`.
|
|
- `GET`, `POST /api/dumps/:id/comments` — anyone who may see the dump; posting
|
|
signed in. Its comments.
|
|
|
|
### playlists
|
|
|
|
- `POST /api/playlists` — signed in. Creates a playlist.
|
|
- `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.
|
|
- `POST`, `DELETE /api/playlists/:id/dumps/:dumpId` — the same. Adds or
|
|
removes a dump.
|
|
- `PUT /api/playlists/:id/order` — the same. Reorders its dumps.
|
|
- `GET`, `PUT /api/playlists/:id/cover` — the cover; a new one, by its owner
|
|
or `playlist:moderate`.
|
|
|
|
### comments
|
|
|
|
- `PATCH`, `DELETE /api/comments/:id` — its author, `comment:moderate`. Edits
|
|
or deletes a comment.
|
|
- `GET /api/comments/:id/likers` — anyone. Who likes it.
|
|
|
|
### categories
|
|
|
|
- `GET /api/categories` — anyone. Every category.
|
|
- `POST /api/categories`, `PUT`, `DELETE /api/categories/:id` —
|
|
`category:manage`. Changes them.
|
|
|
|
### community
|
|
|
|
- `GET /api/community?period=weekly|monthly` — signed in. The site's top
|
|
dumps, new members, new public playlists and totals over the last week (by
|
|
default) or month, as of the server's last refresh.
|
|
|
|
### chat
|
|
|
|
- `GET /api/chat?before=&after=` — signed in. A page of messages. The chat
|
|
itself goes over `/ws`.
|
|
|
|
### invites
|
|
|
|
- `POST /api/invites` — signed in. A new invite.
|
|
- `GET /api/invites/:token` — anyone. Whether it still works.
|
|
|
|
### sessions, password-resets, unsubscribe
|
|
|
|
- `POST /api/sessions` — anyone. Signs in.
|
|
- `POST /api/password-resets` — anyone. Emails a reset link.
|
|
- `PUT /api/password-resets/:token` — with the link's token. A new password.
|
|
- `POST /api/unsubscribe?token=` — with a digest's token. Turns it off. Its
|
|
v3 path, `POST /api/users/unsubscribe`, stays for good, since sent emails'
|
|
`List-Unsubscribe` headers name it.
|
|
|
|
### search
|
|
|
|
- `GET /api/search?q=` — anyone. A page of dumps, and up to 20 users and
|
|
playlists.
|
|
|
|
### attachments
|
|
|
|
- `POST /api/attachments` — signed in. Stores an image.
|
|
- `GET /api/attachments/:id` — anyone who may see the text holding it.
|
|
|
|
### remote
|
|
|
|
- `GET /api/remote/preview?url=` — signed in. A link's preview.
|
|
- `GET /api/remote/image?url=` — anyone. A remote image through this site.
|
|
- `GET /api/remote/bandcamp?url=` — anyone. A Bandcamp page's tracks.
|
|
- `GET /api/remote/youtube?list=` — anyone. A YouTube playlist's tracklist.
|
|
|
|
### /ws
|
|
|
|
- `GET /ws?token=` — anyone; a session token signs it in. The WebSocket of
|
|
live updates and the chat (`shared/ws.ts`).
|