Files
once-campfire/app/controllers/messages_controller.rb
T
Ronald Lokers 3ca1dcbf77 Allow bots to update and destroy their own messages
Bots can only create. A lifecycle notification — an alert that fires and then
resolves, a deploy that starts and finishes, a backup that runs — therefore has
to post a second message, and the room becomes an append-only log of states
rather than a view of the current one.

Adds PATCH and DELETE inside the existing bot_key scope, routed to
Messages::ByBotsController. The body is read the way create reads it, so
updating a message is the same request shape as posting one.

No new authorization: both actions already run through ensure_can_administer,
and can_administer? grants access only to a record the user created, so a bot
key reaches that bot's own messages and no others. set_room narrows it again by
looking the room up through the bot's own memberships. A leaked bot key gains
what it could already do by posting: write to rooms that bot belongs to.

update answers head :ok rather than the redirect, which meant extracting the
update and its broadcast into update_message — calling super and then head
would double render, since the parent redirects inside the action. destroy
needs no split, because the parent renders implicitly like create does.
2026-08-11 13:33:01 +02:00

88 lines
2.1 KiB
Ruby

class MessagesController < ApplicationController
include ActiveStorage::SetCurrent, RoomScoped
before_action :set_room, except: :create
before_action :set_message, only: %i[ show edit update destroy ]
before_action :ensure_can_administer, only: %i[ edit update destroy ]
layout false, only: :index
def index
@messages = find_paged_messages
if @messages.any?
fresh_when @messages
else
head :no_content
end
end
def create
set_room
@message = @room.messages.create_with_attachment!(message_params)
@message.broadcast_create
deliver_webhooks_to_bots
rescue ActiveRecord::RecordNotFound
render action: :room_not_found
end
def show
end
def edit
end
def update
update_message
redirect_to room_message_url(@room, @message)
end
def destroy
@message.destroy
@message.broadcast_remove
end
private
def set_message
@message = @room.messages.find(params[:id])
end
# Extracted so bots can reuse the update and its broadcast while answering with
# a status code instead of a redirect.
def update_message
@message.update!(message_params)
@message.broadcast_replace_to @room, :messages, target: [ @message, :presentation ], partial: "messages/presentation", attributes: { maintain_scroll: true }
end
def ensure_can_administer
head :forbidden unless Current.user.can_administer?(@message)
end
def find_paged_messages
case
when params[:before].present?
@room.messages.with_creator.page_before(@room.messages.find(params[:before]))
when params[:after].present?
@room.messages.with_creator.page_after(@room.messages.find(params[:after]))
else
@room.messages.with_creator.last_page
end
end
def message_params
params.require(:message).permit(:body, :attachment, :client_message_id)
end
def deliver_webhooks_to_bots
bots_eligible_for_webhook.excluding(@message.creator).each { |bot| bot.deliver_webhook_later(@message) }
end
def bots_eligible_for_webhook
@room.direct? ? @room.users.active_bots : @message.mentionees.active_bots
end
end