mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-08-12 18:10:43 +09:00
3ca1dcbf77
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.
88 lines
2.1 KiB
Ruby
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
|