Room#destroy destroyed every message inside the room's own
transaction, which holds SQLite's write lock until the last one: on a
room with many messages, every other write in the app waited and
failed. The request now takes the room away from its members and
leaves the rest to Room::DestroyJob, which destroys the messages one
at a time, each in its own short transaction, and then the room.
An open room is closed in the request, so that someone who joins the
account before the job ends isn't given it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bj8KnxpTf9sj2Ysa8aLAVa
Rails main compiles HTML templates through Herb under the 8.2 framework
defaults, which Campfire loads. Herb rejects `case` and its first `when`
in a single ERB tag, so the three pwa/ partials failed to compile, and
`herb:check` rejects ERB output in attribute names, which the account
settings' switch used for `checked`. Give `case` its own tag and build
the switch with tag.input; both render the same under Erubi.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 244e77241b)
A preview belongs to the page it previews, so both URLs point somewhere
else. An absolute URL on our own host passed the scheme and host checks,
and every reader's browser fetched it with their session attached, which
turns a message into a GET request made on the reader's behalf.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186eyzivcTn6wqjEE4Wnxdt
A link preview's link and image come from attributes on the message body,
which the composer fills in from the unfurl the server performed. A body
written by hand can put anything in those attributes, and the preview
partial rendered them as they were.
Keep the link and the image only when they parse as absolute http or https
URLs, so nothing in a message body can aim either one at another scheme or
at a path on this Campfire, and render the title and the description as
text.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186eyzivcTn6wqjEE4Wnxdt