Files
danbooru/app/controllers/posts_controller.rb
evazion 36985e9590 api: add rate limit to /posts.atom endpoint.
Add a rate limit of 1 request per 2 seconds to the post RSS feed
endpoint (/posts.atom).

This lets you check your feeds 30 times per minute, or 1800 times per
hour. The previous limit was 10 requests per second.

This is because there are too many bad RSS feed reader bots constantly
checking the same tags over and over again, 24 hours a day, as fast
they can.
2021-12-10 01:46:01 -06:00

142 lines
4.7 KiB
Ruby

class PostsController < ApplicationController
respond_to :html, :xml, :json, :js
layout "sidebar"
rate_limit :index, rate: 1.0/2.seconds, burst: 50, if: -> { request.format.atom? }, key: "posts:index.atom"
def index
if params[:md5].present?
@post = authorize Post.find_by!(md5: params[:md5])
respond_with(@post) do |format|
format.html { redirect_to(@post) }
end
elsif params[:random].to_s.truthy?
post_set = PostSets::Post.new(params[:tags], params[:page], params[:limit], format: request.format.symbol)
query = "#{post_set.normalized_query.to_s} random:#{post_set.per_page}".strip
redirect_to posts_path(tags: query, page: params[:page], limit: params[:limit], format: request.format.symbol)
else
tag_query = params[:tags] || params.dig(:post, :tags)
@show_votes = (params[:show_votes].presence || cookies[:post_preview_show_votes].presence || "false").truthy?
@post_set = PostSets::Post.new(tag_query, params[:page], params[:limit], format: request.format.symbol, show_votes: @show_votes)
@preview_size = params[:size].presence || cookies[:post_preview_size].presence || PostPreviewComponent::DEFAULT_SIZE
@posts = authorize @post_set.posts, policy_class: PostPolicy
@post_set.log!
respond_with(@posts) do |format|
format.atom
end
end
end
def show
@post = authorize Post.find(params[:id])
if request.format.html?
include_deleted = @post.is_deleted? || (@post.parent_id.present? && @post.parent.is_deleted?) || CurrentUser.user.show_deleted_children?
@sibling_posts = @post.parent.present? ? @post.parent.children : Post.none
@sibling_posts = @sibling_posts.undeleted unless include_deleted
@sibling_posts = @sibling_posts.includes(:media_asset)
@child_posts = @post.children
@child_posts = @child_posts.undeleted unless include_deleted
@sibling_posts = @sibling_posts.includes(:media_asset)
end
respond_with(@post) do |format|
format.html.tooltip { render layout: false }
end
end
def show_seq
authorize Post
context = PostSearchContext.new(params)
if context.post_id
redirect_to(post_path(context.post_id, q: params[:q]))
else
redirect_to(post_path(params[:id], q: params[:q]))
end
end
def update
@post = authorize Post.find(params[:id])
@post.update(permitted_attributes(@post))
@show_votes = (params[:show_votes].presence || cookies[:post_preview_show_votes].presence || "false").truthy?
@preview_size = params[:size].presence || cookies[:post_preview_size].presence || PostPreviewComponent::DEFAULT_SIZE
respond_with_post_after_update(@post)
end
def destroy
@post = authorize Post.find(params[:id])
if params[:commit] == "Delete"
move_favorites = params.dig(:post, :move_favorites).to_s.truthy?
@post.delete!(params.dig(:post, :reason), move_favorites: move_favorites, user: CurrentUser.user)
flash[:notice] = "Post deleted"
end
respond_with_post_after_update(@post)
end
def revert
@post = authorize Post.find(params[:id])
@version = @post.versions.find(params[:version_id])
@post.revert_to!(@version)
respond_with(@post) do |format|
format.js
end
end
def copy_notes
@post = Post.find(params[:id])
@other_post = authorize Post.find(params[:other_post_id].to_i)
@post.copy_notes_to(@other_post)
if @post.errors.any?
@error_message = @post.errors.full_messages.join("; ")
render :json => {:success => false, :reason => @error_message}.to_json, :status => 400
else
head 204
end
end
def random
@post = Post.user_tag_match(params[:tags]).random(1).take
raise ActiveRecord::RecordNotFound if @post.nil?
authorize @post
respond_with(@post) do |format|
format.html { redirect_to post_path(@post, :tags => params[:tags]) }
end
end
def mark_as_translated
@post = authorize Post.find(params[:id])
@post.mark_as_translated(params[:post])
respond_with_post_after_update(@post)
end
private
def respond_with_post_after_update(post)
respond_with(post) do |format|
format.html do
if post.warnings.any?
flash[:notice] = post.warnings.full_messages.join(".\n \n")
end
if post.errors.any?
@error_message = post.errors.full_messages.join("; ")
render :template => "static/error", :status => 500
else
response_params = {:q => params[:tags_query], :pool_id => params[:pool_id], :favgroup_id => params[:favgroup_id]}
response_params.reject! {|_key, value| value.blank?}
redirect_to post_path(post, response_params)
end
end
format.json do
render :json => post.to_json
end
end
end
end