khannurien
|
d392be7cb8
|
v3: api/ gets one layout rule and keeps to it, and whatever crosses the wire is defined once, in shared/ — api/model/interfaces.ts and src/model.ts declared the same 40 types by hand and had drifted: the error envelope, email required on one side and optional on the other, Invitee/InviteTreeEntry and LinkPreview/UrlPreviewResponse for one shape each. The validation limits, the role permissions, the chat length cap, the @mention pattern and the Bandcamp and YouTube player responses were copied too, behind "keep in sync" comments. shared/ now holds them once, one file per domain (api, category, chat, comment, dump, follow, mention, notification, permissions, player, playlist, search, user, validation, ws), and imports nothing from either half. The server sends Dates; the client reads Wire<T>, the same type with every Date an ISO string, and turns it back with src/utils/shared/deserialize.ts. APIErrorCode becomes a const object, since the frontend's erasableSyntaxOnly rejects an enum. The shared types showed two real gaps: the avatar upload and GET /api/users/me return the full self user and the frontend read them as public users, and a stored session may predate media tokens, so the client keeps it as its own Session type. The backend rule: each folder holds one kind of module, each file is named after its domain. routes/ parse and answer; services/ hold the logic and every SQL query (lib/auth.ts's token check moves to user-service); remote/ is the only place that fetches from other sites — the guarded fetch and the HTML scraping out of rich-content-service, public-address, the Bandcamp and YouTube resolvers, the link providers, the image proxy (fetch, checks and cache, out of routes/preview.ts) and the Twemoji glyphs (out of lib/site-icons.ts). model/ holds per table a row type, its guard and its mappers; validators/ the request guards; db/connection.ts the handle and transaction(), db/startup.ts the purges and default admin that db.ts ran on import, called at the same moment. Tests mirror the source one file per module, test/<layer>/<module>.test.ts, with shared/'s under shared/test/ (deno task test runs both); the test that covered the bandcamp and soundcloud providers together is two now, and the backend can() has its own, so 202 tests. deno lint src/ is clean too: window.* becomes globalThis.*, and test bodies and act() callbacks that never awaited lose their async. The README states the rule. No behaviour changes; the catalogues only move their source references.
Build and Publish Docker Image / build-and-push (push) Successful in 50s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-09-27 20:19:48 +00:00 |
|
khannurien
|
25601edaf8
|
v3: the backend gets a test suite, and the bugs it turned up are fixed — deno task test runs 186 tests over api/ in about 18s, against a throwaway database and uploads directory (GERBEUR_DB_PATH and GERBEUR_UPLOADS_DIR now override the defaults; api/test/setup.ts sets them before any app module loads), with requests sent through the whole middleware stack in-process via app.handle, outbound fetches and DNS stubbed, and the socket tests on a real Oak listener since an upgraded request has no response for handle() to return. It covers auth (token purposes, revocation after a password change, live roles, login rate limiting, invites claimed once even under parallel registrations, password resets), dumps end to end (create, privacy, by-url matching on the canonical key, backlinks, categories, edits, file replacement, thumbnails, refresh, delete), file serving headers and ranges, attachments, avatars, comments and likes, playlists, follows and feeds, notifications and mentions, categories, search, chat cursors and read markers, link previews and the SSRF guard on every redirect hop, the Bandcamp and YouTube resolvers, migrations (a fresh schema is left untouched; the data rewrites in 0004/0005 and 0010–0013 do what they say), CORS, OG tags, static caching, and the WebSocket protocol. Fixed along the way: an unknown category id on a new dump returned 400 but kept the dump row, and for a file dump deleted the file while keeping the row; an edit or file replacement with one was half-applied before the 400 — categories are now checked before anything is written. A suffix range (bytes=-3) was read as an end offset and got a 416 instead of the last three bytes. Reordering a playlist accepted a list naming a member twice. verifyPassword threw on a stored hash without a salt instead of answering false. And a request with no Accept-Encoding, which RFC 9110 lets a server treat as accepting anything, was sent the brotli sibling; curl and bots now get the plain file.
Build and Publish Docker Image / build-and-push (push) Successful in 46s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-09-27 12:31:05 +00:00 |
|
khannurien
|
2ad8ae73d5
|
v3: the API stops trusting what it's handed — a review of api/ turned up a server anyone could crash, uploads that ran script on the app's origin, and a server that would fetch its own network on request, and this closes them along with the smaller holes around them. A WebSocket message that wasn't an object (a bare null will do) threw inside the listener, which Deno treats as uncaught and exits on; messages are now shape-checked and the handler can't throw out. Uploaded files kept the MIME type the uploader's browser claimed, so an .html or .svg dump opened from /api/files ran with the session token in reach; only images, audio, video, PDF and plain text are served inline now, the rest download, and every file carries nosniff and a sandboxing CSP (PDFs aside, which Chrome won't render sandboxed). The image proxy passed image/svg+xml straight through the same way and now takes raster formats only. Outbound fetches for previews, the proxy and Bandcamp checked the hostname against a partial list — no 169.254.169.254, no 0.0.0.0, an IPv6 check that never matched a bracketed hostname, nothing for names that resolve inward, and redirects followed blindly; every hop now resolves and is refused if any address is private or reserved, redirects are walked by hand, the curl fallback is pinned to the checked IP, and /api/preview needs a login. PATCH /users/me accepted a new password without the current one and now takes only the description and email; a wrong password at login answered 500 because the handler caught its own 401, and now says 401. Changing or resetting a password now ends every older session through users.tokens_valid_after (migration 0015) and an iat on every token, with change-password handing back fresh tokens the modal keeps, and all token checks go through one authenticate() that also refuses deleted users. Invites and reset links are claimed atomically before the async hash rather than marked after, so one invite no longer opens several accounts, and an email address belongs to one account. File and thumbnail URLs carry a media-only token rather than the session token, so a URL in a log exposes files and not the account; older sessions fetch one on load. Private things stop leaking into broadcasts: private playlist updates go to their owner, votes and likes on someone else's private dump 404 and their counts go to the owner alone, and a private dump can't be slipped into a playlist. Smaller: selfProvider only claims this site's own host, so github.com/users/bob stops rendering our bob; the chat read marker can't be parked in the future on an empty chat; dump mutations use the resolved id so a slug works; multipart dumps get the title and comment limits; logins and reset requests are rate-limited through a shared token bucket; the password compare is constant-time; ffmpeg reads only local files in known video containers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
2026-09-24 20:07:33 +00:00 |
|
khannurien
|
c29f45fbc8
|
v3: added index default tab selection setting, fixed notification pages not loading, reduced invite token length, global player state survives page reloads, many visual fixes
Build and Publish Docker Image / build-and-push (push) Successful in 44s
|
2026-06-27 19:44:35 +00:00 |
|
khannurien
|
76de798faf
|
v3: fixed authorization bug with stale tokens, fixed private dump access bug, various small fixes across the app
Build and Publish Docker Image / build-and-push (push) Failing after 20s
|
2026-06-27 10:40:07 +00:00 |
|
khannurien
|
20b9bfe7b4
|
v3: added password change/reset feature
|
2026-04-06 16:30:00 +00:00 |
|
khannurien
|
cbb3505139
|
v3: search engine, responsive header with compact user menu
|
2026-03-29 11:56:31 +00:00 |
|
khannurien
|
ca70bdc14b
|
v3: added opengraph support to the app, wrote README instructions incl. a Docker image
|
2026-03-26 19:55:48 +00:00 |
|
khannurien
|
fbbbb43258
|
v3: code quality pass, various bug fixes
|
2026-03-23 07:47:49 +00:00 |
|
khannurien
|
608c6bc6a8
|
v3: follows, notifications, invite-only registration, unread markers
|
2026-03-21 18:42:47 +00:00 |
|
khannurien
|
867e64cb5b
|
v1 review pass: fixed some minor bugs
|
2026-03-16 11:08:39 +00:00 |
|
khannurien
|
6207a7549f
|
initial commit, boilerplate stuff
|
2026-03-15 17:15:46 +00:00 |
|