khannurien
16b510764c
v3: the frontend ships compressed, caches for good, and stops reaching for other people's servers — a review of the bundle's size found the server was the biggest cost. Oak's send() compressed nothing and sent every hashed chunk with max-age=0, so a first visit to the home page moved about 710 KB of JS and CSS where 200 KB would do, and every later one revalidated some thirty files. The build now writes .br and .gz next to every JS, CSS, JSON and SVG file (a precompress plugin, brotli 11), which send() already serves when the browser accepts them; /assets and /emojibase are cached for a year as immutable, index.html is no-cache, and static responses carry Vary: Accept-Encoding. A missing file under either prefix is a 404 rather than index.html with a 200, which an open tab after a deploy used to try to run as a script; the app now reloads on vite:preloadError, at most once a minute. manualChunks sent everything in node_modules to one vendor chunk, so react-hook-form and frimousse loaded with every page though only the lazy form and editor chunks use them, and its markdown rule missed hast, vfile, unified and property-information while every feed loads Markdown through DumpCard anyway; only react, react-dom, scheduler and react-router get a chunk of their own now, the rest is left to the bundler. Up-front JS goes from 133 to 96 KB gzip, the home page from 178 to 160. Fonts: index.html blocked rendering on a Google Fonts stylesheet, opening two more hosts and handing each visitor's IP to Google; they come from @fontsource now, with src/fonts.css declaring only the latin and latin-ext faces (the packages' own CSS declares every subset, which added 6 KB gzip to the stylesheet), so Vite hashes the woff2 files into /assets and a browser still only downloads its theme's font. The families are the variable builds ("Saira Variable" and so on); Fraunces keeps its opsz, SOFT and WONK axes. Emoji: frimousse fetched emojibase-data@latest from jsDelivr, unpinned and third-party; emojibase-data 17.0.0 is a dependency now, its data and messages for each lingui locale are emitted under /emojibase/17.0.0 (served from node_modules in dev), and the picker is pointed there.
...
Build and Publish Docker Image / build-and-push (push) Successful in 45s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-09-27 11:44:08 +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
fb8364e24d
v3: reworked posting — three panels, drafts, duplicate detection, upload progress
...
Build and Publish Docker Image / build-and-push (push) Successful in 42s
Posting a dump was a single form where the important choices were the easiest
to miss. It is now three panels: link or file, why & where, playlists.
Composition:
- No more URL/File toggle. An empty panel offers both ways in at once and the
kind follows what you actually did; a file dropped anywhere in the modal is
accepted, not just on the zone.
- Categories and visibility get their own panel instead of a disclosure that
read as optional, and the primary button stays "Next" until they've been
seen. Visibility carries a real label now.
- The draft (link, title, why, categories, visibility) is mirrored to
localStorage on every change and restored on reopen, so Escape or a stray
backdrop click costs nothing. Only an attached file can't be restored, so
that is the one case that asks before closing.
- URL dumps can carry a poster-supplied title instead of being stuck with
whatever the page scraped, editable right under the preview.
- Multipart uploads go through XHR so there is a real progress bar and a
percentage on the button, rather than 50 MB of silence.
Duplicates:
- New dumps.url_canonical column (+ index, backfilled by 0013) holding a lossy
key that ignores scheme, www., trailing slashes, tracking parameters and
YouTube share shapes. GET /api/dumps/by-url reads it, and the create form
warns "already dumped by X" while it fetches the preview. Never blocking.
Fixes:
- /api/preview now reports whether the page was actually reached: a failed
fetch still yields a hostname-only stub, so a dead link and a page without
metadata used to render identically.
- The Web Share Target never worked. The manifest posts to "/", but the index
redirect dropped the query string, so every Android share landed on the feed
with nothing pre-filled.
- File dumps no longer take the extension into their title.
- The link field no longer autofocuses on touch, where it raised a keyboard
over the modal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
Claude-Session: https://claude.ai/code/session_01TiAPtJZeCYYk8rKehtLUQU
2026-09-08 14:40:37 +00:00
khannurien
2606ef4ccc
v3: native bandcamp playback and a queue in the global player
...
Build and Publish Docker Image / build-and-push (push) Successful in 43s
The player now holds a queue instead of a single item, so albums play
through and single tracks share the same code path. Adds a "stream" item
kind for expiring, remotely-hosted sources: Bandcamp signs its mp3 URLs
for ~24h, so an item carries the page it came from and re-resolves itself
on error or on a stale restored session, falling back to the iframe embed
if that fails too.
Gated behind GERBEUR_BANDCAMP_PLAYER (default "embed"), injected into a
meta tag the same way as site-name/site-emoji.
Also: player header gains artwork and a subtitle, volume persists across
queue advances and reloads, and cross-origin streams get a plain seek bar
rather than a waveform that can never decode.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
Claude-Session: https://claude.ai/code/session_01SbtjNsT5wvuABnfqhegZEJ
2026-08-22 12:14:28 +00:00
khannurien
6c4f410d9b
v3: added a chatbox
Build and Publish Docker Image / build-and-push (push) Has been cancelled
2026-06-29 20:25:10 +00:00
f171027673
v3: added customizable site emoji (logo), small visual tweaks
Build and Publish Docker Image / build-and-push (push) Successful in 41s
2026-06-29 15:12:25 +02:00
khannurien
fae25f3e6c
v3: added site-wide categories, added admin category management, various visual fixes
Build and Publish Docker Image / build-and-push (push) Successful in 2m52s
2026-06-28 16:23:44 +00:00
khannurien
551e29c564
v3: added playlist moderation permission
Build and Publish Docker Image / build-and-push (push) Successful in 43s
2026-06-28 06:37:23 +00:00
khannurien
810faaf21a
v3: added a roles/permissions system, added user management tab on user profile
Build and Publish Docker Image / build-and-push (push) Successful in 42s
2026-06-28 06:21:23 +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
ed7695663e
v3: correctly using SITE_NAME across the app, added notifications on comments
Build and Publish Docker Image / build-and-push (push) Successful in 44s
2026-04-08 20:12:30 +00:00
khannurien
856511777c
chore: smaller docker image, simplification pass on environment variables, fix direct navigation without vite
Build and Publish Docker Image / build-and-push (push) Successful in 46s
2026-04-08 19:43:19 +00:00
khannurien
20b9bfe7b4
v3: added password change/reset feature
2026-04-06 16:30:00 +00:00
khannurien
378b3ffa46
v3: added onboarding email on account creation
2026-03-30 14:55:30 +00:00
khannurien
cbb3505139
v3: search engine, responsive header with compact user menu
2026-03-29 11:56:31 +00:00
khannurien
f0f6472db6
v3: added attachments to resources, allow users to paste images into TextEditor, strengthened WS reliability
2026-03-27 08:53:32 +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
0cb5a798c7
v3: added journal view to the index, code organization pass
2026-03-25 21:07:17 +00:00
khannurien
fbbbb43258
v3: code quality pass, various bug fixes
2026-03-23 07:47:49 +00:00
khannurien
34e908d1bc
v3: added content slugs, fixed real-time updates in client, added @mentions across the app, added new file selector and drop zone
2026-03-22 16:06:26 +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