v3: the backend is documented for deno doc, one way in every file, and the docs build and publish themselves — comments were in three shapes: file headers as // lines, as an untagged /** */ (which deno doc drops, only an @module block counts) or missing; 43 exports described by a // deno doc can't see, two JSDoc blocks sitting on the wrong declaration and one duplicated; endpoints commented in 5 of 18 route files, three ways, on router.get() calls no doc tool reads. The rule, applied to every non-test module of api/, migrations included: each opens with a /** … @module */ block; every export has JSDoc, and so does every field of an exported interface; a comment describing a declaration is /** */, // only notes code; functions open with a verb, everything else with a noun phrase; parameters are named in the prose, {@link} only for symbols in scope; whatever throws to its caller says so with @throws (for an APIException, the statuses and when); a router's JSDoc lists its endpoints, method and path, who may call it, what it does. deno doc --lint goes from 528 errors to none in api/ (shared/ keeps its 385; it is next). The lint also wanted explicit types on config.ts's settings and a few return types, JsonLdResult exported, and the routers typed Router. deno task doc builds the HTML reference into docs/ from every module of api/ and shared/ but the tests, deno task doc:lint lints it; the index page is the module doc of a root mod.ts (deno.json's exports), which introduces the backend, shared/ and the frontend to come and links every module, and mod.test.ts fails when one is missing. The docs workflow builds it on every push to main that touches them and publishes it to the gh-pages branch, like publish-assistant's. Two defects found along the way are fixed: a private playlist's cover was served to anyone who had its id or slug, and now needs its owner's media token, as private dump files do (playlistImageUrl adds it for private playlists only, so public cover URLs stay the same for everyone); og.ts and static.ts each filled index.html's placeholders, and now share lib/index-html.ts, so the OG middleware picks up a rebuilt index.html instead of keeping the first it read. The README states the documentation rule. 205 backend and 628 frontend tests pass.
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 47s
Build and deploy docs / build-and-push (push) Successful in 26s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-09-27 21:07:34 +00:00
parent 1702370447
commit d62e4a9883
119 changed files with 2967 additions and 611 deletions

162
mod.ts Normal file
View File

@@ -0,0 +1,162 @@
/**
* # gerbeur
*
* A small invite-only social platform for sharing links and files. The
* repository holds three parts: the backend in `api/`, the frontend in `src/`,
* and in `shared/` whatever crosses the wire between them. This reference
* documents the backend and the shared modules; the frontend isn't in it yet.
*
* ## Backend — `api/`
*
* An Oak server over SQLite that answers the API and the WebSocket, and serves
* the built frontend. A request goes through a route, which parses it and
* answers. The route calls services, which hold the logic and every SQL query;
* they read rows through the model, and reach other sites only through
* `remote/`.
*
* - **Entry point** — builds the application and listens:
* [main](./api/main.ts/index.html).
* - **Routes** — the HTTP API; each router's page lists its endpoints:
* [attachments](./api/routes/attachments.ts/index.html),
* [avatars](./api/routes/avatars.ts/index.html),
* [bandcamp](./api/routes/bandcamp.ts/index.html),
* [categories](./api/routes/categories.ts/index.html),
* [chat](./api/routes/chat.ts/index.html),
* [comments](./api/routes/comments.ts/index.html),
* [dumps](./api/routes/dumps.ts/index.html),
* [files](./api/routes/files.ts/index.html),
* [follows](./api/routes/follows.ts/index.html),
* [invites](./api/routes/invites.ts/index.html),
* [notifications](./api/routes/notifications.ts/index.html),
* [playlists](./api/routes/playlists.ts/index.html),
* [preview](./api/routes/preview.ts/index.html),
* [search](./api/routes/search.ts/index.html),
* [thumbnails](./api/routes/thumbnails.ts/index.html),
* [users](./api/routes/users.ts/index.html),
* [ws](./api/routes/ws.ts/index.html),
* [youtube](./api/routes/youtube.ts/index.html).
* - **Middleware** — what runs around every route:
* [auth](./api/middleware/auth.ts/index.html),
* [error](./api/middleware/error.ts/index.html),
* [og](./api/middleware/og.ts/index.html).
* - **Services** — the logic and every SQL query, one module per domain:
* [attachment-service](./api/services/attachment-service.ts/index.html),
* [backlink-service](./api/services/backlink-service.ts/index.html),
* [category-service](./api/services/category-service.ts/index.html),
* [chat-service](./api/services/chat-service.ts/index.html),
* [comment-like-service](./api/services/comment-like-service.ts/index.html),
* [comment-service](./api/services/comment-service.ts/index.html),
* [dump-service](./api/services/dump-service.ts/index.html),
* [email-service](./api/services/email-service.ts/index.html),
* [follow-service](./api/services/follow-service.ts/index.html),
* [invite-service](./api/services/invite-service.ts/index.html),
* [notification-service](./api/services/notification-service.ts/index.html),
* [password-reset-service](./api/services/password-reset-service.ts/index.html),
* [playlist-service](./api/services/playlist-service.ts/index.html),
* [rich-content-service](./api/services/rich-content-service.ts/index.html),
* [user-service](./api/services/user-service.ts/index.html),
* [vote-service](./api/services/vote-service.ts/index.html),
* [ws-service](./api/services/ws-service.ts/index.html).
* - **Model** — per table, its row type, guard and mappers:
* [auth](./api/model/auth.ts/index.html),
* [category](./api/model/category.ts/index.html),
* [chat](./api/model/chat.ts/index.html),
* [comment](./api/model/comment.ts/index.html),
* [dump](./api/model/dump.ts/index.html),
* [follow](./api/model/follow.ts/index.html),
* [invite](./api/model/invite.ts/index.html),
* [notification](./api/model/notification.ts/index.html),
* [password-reset](./api/model/password-reset.ts/index.html),
* [playlist](./api/model/playlist.ts/index.html),
* [user](./api/model/user.ts/index.html).
* - **Validators** — the request-body guards:
* [category](./api/validators/category.ts/index.html),
* [comment](./api/validators/comment.ts/index.html),
* [dump](./api/validators/dump.ts/index.html),
* [playlist](./api/validators/playlist.ts/index.html),
* [user](./api/validators/user.ts/index.html).
* - **Remote** — the only place that fetches from other sites:
* [bandcamp-streams](./api/remote/bandcamp-streams.ts/index.html),
* [bandcamp-tralbum](./api/remote/bandcamp-tralbum.ts/index.html),
* [fetch](./api/remote/fetch.ts/index.html),
* [html](./api/remote/html.ts/index.html),
* [image-proxy](./api/remote/image-proxy.ts/index.html),
* [public-address](./api/remote/public-address.ts/index.html),
* [twemoji](./api/remote/twemoji.ts/index.html),
* [youtube-playlist](./api/remote/youtube-playlist.ts/index.html).
* - **Link providers** — the previews of each kind of site:
* [bandcamp](./api/remote/providers/bandcamp.ts/index.html),
* [generic](./api/remote/providers/generic.ts/index.html),
* [provider](./api/remote/providers/provider.ts/index.html),
* [self](./api/remote/providers/self.ts/index.html),
* [soundcloud](./api/remote/providers/soundcloud.ts/index.html),
* [youtube](./api/remote/providers/youtube.ts/index.html).
* - **Lib** — helpers that belong to no layer:
* [api-exception](./api/lib/api-exception.ts/index.html),
* [auth](./api/lib/auth.ts/index.html),
* [canonical-url](./api/lib/canonical-url.ts/index.html),
* [index-html](./api/lib/index-html.ts/index.html),
* [jwt](./api/lib/jwt.ts/index.html),
* [pagination](./api/lib/pagination.ts/index.html),
* [permissions](./api/lib/permissions.ts/index.html),
* [playlist-members](./api/lib/playlist-members.ts/index.html),
* [rate-limit](./api/lib/rate-limit.ts/index.html),
* [reserved-slugs](./api/lib/reserved-slugs.ts/index.html),
* [self-url](./api/lib/self-url.ts/index.html),
* [serve-file](./api/lib/serve-file.ts/index.html),
* [site-icons](./api/lib/site-icons.ts/index.html),
* [slugify](./api/lib/slugify.ts/index.html),
* [static](./api/lib/static.ts/index.html),
* [upload](./api/lib/upload.ts/index.html).
* - **Database** — the handle, its creation, migration and startup
* housekeeping: [connection](./api/db/connection.ts/index.html),
* [init](./api/db/init.ts/index.html),
* [migrate](./api/db/migrate.ts/index.html),
* [startup](./api/db/startup.ts/index.html).
* - **Migrations** — applied in order, each once:
* [0001_comment_likes](./api/db/migrations/0001_comment_likes.ts/index.html),
* [0002_dump_thumbnail](./api/db/migrations/0002_dump_thumbnail.ts/index.html),
* [0003_dump_backlinks](./api/db/migrations/0003_dump_backlinks.ts/index.html),
* [0004_user_roles](./api/db/migrations/0004_user_roles.ts/index.html),
* [0005_drop_is_admin](./api/db/migrations/0005_drop_is_admin.ts/index.html),
* [0006_categories](./api/db/migrations/0006_categories.ts/index.html),
* [0007_password_reset_tokens](./api/db/migrations/0007_password_reset_tokens.ts/index.html),
* [0008_chat_messages](./api/db/migrations/0008_chat_messages.ts/index.html),
* [0009_chat_reply](./api/db/migrations/0009_chat_reply.ts/index.html),
* [0010_youtube_embed_start](./api/db/migrations/0010_youtube_embed_start.ts/index.html),
* [0011_split_favicon_thumbnail](./api/db/migrations/0011_split_favicon_thumbnail.ts/index.html),
* [0012_fix_favicon_reclassification](./api/db/migrations/0012_fix_favicon_reclassification.ts/index.html),
* [0013_dump_url_canonical](./api/db/migrations/0013_dump_url_canonical.ts/index.html),
* [0014_chat_read_state](./api/db/migrations/0014_chat_read_state.ts/index.html),
* [0015_token_revocation](./api/db/migrations/0015_token_revocation.ts/index.html),
* [0016_performance_indexes](./api/db/migrations/0016_performance_indexes.ts/index.html).
* - **Settings and scripts**: [config](./api/config.ts/index.html),
* [generate-icons](./api/scripts/generate-icons.ts/index.html).
*
* ## Shared — `shared/`
*
* The types and constants both halves use: the API envelope, each domain's
* types and request bodies, the socket protocol, the validation limits and the
* role permissions. It imports nothing from either half. Its modules aren't
* documented yet.
*
* Modules: [api](./shared/api.ts/index.html),
* [category](./shared/category.ts/index.html),
* [chat](./shared/chat.ts/index.html),
* [comment](./shared/comment.ts/index.html),
* [dump](./shared/dump.ts/index.html), [follow](./shared/follow.ts/index.html),
* [mention](./shared/mention.ts/index.html),
* [notification](./shared/notification.ts/index.html),
* [permissions](./shared/permissions.ts/index.html),
* [player](./shared/player.ts/index.html),
* [playlist](./shared/playlist.ts/index.html),
* [search](./shared/search.ts/index.html), [user](./shared/user.ts/index.html),
* [validation](./shared/validation.ts/index.html),
* [ws](./shared/ws.ts/index.html).
*
* ## Frontend — `src/`
*
* The React app, built with Vite. Not in this reference yet.
*
* @module
*/