uploads: add limit to prevent users from submitting too many uploads at once.

Add a limit so that users can't upload more if they already have more
than 250 images queued for upload.

For example, if you upload a Pixiv post that has 200 images, then you'll
have 200 queued images for upload. This will go down as the images are
processed. If you exceed the limit, then trying to create new uploads
will return an error.

This is to prevent single users from overwhelming the site by uploading
too many images at once, thereby preventing other users from uploading
because the job queue is backed up and can't process new uploads by
other users until existing uploads are finished.
This commit is contained in:
evazion
2022-02-28 22:19:40 -06:00
parent 7031fd13d7
commit 03560bafc6
3 changed files with 18 additions and 1 deletions

View File

@@ -6,6 +6,9 @@ class Upload < ApplicationRecord
MAX_FILES_PER_UPLOAD = 100 MAX_FILES_PER_UPLOAD = 100
# The maximum number of 'pending' or 'processing' media assets a single user can have at once.
MAX_QUEUED_ASSETS = 250
attr_accessor :files attr_accessor :files
belongs_to :uploader, class_name: "User" belongs_to :uploader, class_name: "User"
@@ -19,6 +22,7 @@ class Upload < ApplicationRecord
validates :source, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { source.present? } validates :source, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { source.present? }
validates :referer_url, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { referer_url.present? } validates :referer_url, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { referer_url.present? }
validate :validate_file_and_source, on: :create validate :validate_file_and_source, on: :create
validate :uploader_is_not_limited, on: :create
after_create :async_process_upload! after_create :async_process_upload!
@@ -64,6 +68,14 @@ class Upload < ApplicationRecord
errors.add(:base, "No file or source given") errors.add(:base, "No file or source given")
end end
end end
def uploader_is_not_limited
queued_asset_count = uploader.upload_media_assets.unfinished.count
if queued_asset_count > MAX_QUEUED_ASSETS
errors.add(:base, "You have too many images queued for upload (queued: #{queued_asset_count}; limit: #{MAX_QUEUED_ASSETS}). Try again later.")
end
end
end end
concerning :SourceMethods do concerning :SourceMethods do

View File

@@ -23,6 +23,9 @@ class UploadMediaAsset < ApplicationRecord
failed: 300, failed: 300,
} }
scope :unfinished, -> { where(status: %w[pending processing]) }
scope :finished, -> { where(status: %w[active failed]) }
def self.visible(user) def self.visible(user)
if user.is_admin? if user.is_admin?
all all

View File

@@ -109,6 +109,7 @@ class User < ApplicationRecord
validate :validate_custom_css, if: :custom_style_changed? validate :validate_custom_css, if: :custom_style_changed?
before_validation :normalize_blacklisted_tags before_validation :normalize_blacklisted_tags
before_create :promote_to_owner_if_first_user before_create :promote_to_owner_if_first_user
has_many :artist_versions, foreign_key: :updater_id has_many :artist_versions, foreign_key: :updater_id
has_many :artist_commentary_versions, foreign_key: :updater_id has_many :artist_commentary_versions, foreign_key: :updater_id
has_many :comments, foreign_key: :creator_id has_many :comments, foreign_key: :creator_id
@@ -131,7 +132,6 @@ class User < ApplicationRecord
has_many :purchased_upgrades, class_name: "UserUpgrade", foreign_key: :purchaser_id, dependent: :destroy has_many :purchased_upgrades, class_name: "UserUpgrade", foreign_key: :purchaser_id, dependent: :destroy
has_many :user_events, dependent: :destroy has_many :user_events, dependent: :destroy
has_one :active_ban, -> { active }, class_name: "Ban" has_one :active_ban, -> { active }, class_name: "Ban"
has_one :email_address, dependent: :destroy has_one :email_address, dependent: :destroy
has_many :api_keys, dependent: :destroy has_many :api_keys, dependent: :destroy
has_many :note_versions, :foreign_key => "updater_id" has_many :note_versions, :foreign_key => "updater_id"
@@ -145,6 +145,8 @@ class User < ApplicationRecord
has_many :ip_bans, foreign_key: :creator_id has_many :ip_bans, foreign_key: :creator_id
has_many :tag_aliases, foreign_key: :creator_id has_many :tag_aliases, foreign_key: :creator_id
has_many :tag_implications, foreign_key: :creator_id has_many :tag_implications, foreign_key: :creator_id
has_many :uploads, foreign_key: :uploader_id, dependent: :destroy
has_many :upload_media_assets, through: :uploads, dependent: :destroy
belongs_to :inviter, class_name: "User", optional: true belongs_to :inviter, class_name: "User", optional: true
accepts_nested_attributes_for :email_address, reject_if: :all_blank, allow_destroy: true accepts_nested_attributes_for :email_address, reject_if: :all_blank, allow_destroy: true