Add 1GB storage cap with staff bypass (#2729)
Re-applies the storage limit from #2713 (reverted in #2715) with an exception for staff identities so our own account isn't blocked.
This commit is contained in:
@@ -543,4 +543,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-perma__account-limit-message {
|
||||||
|
background-color: var(--color-canvas);
|
||||||
|
border: 2px solid var(--color-container);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-block-start: calc(var(--padding-block) / -2);
|
||||||
|
padding: 1ch 2ch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,4 +220,12 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment-by-system--account-limit {
|
||||||
|
--stripe-color: oklch(var(--lch-blue-lightest));
|
||||||
|
|
||||||
|
.comment__content {
|
||||||
|
padding: 3ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
<%= messages_tag(card) do %>
|
<%= messages_tag(card) do %>
|
||||||
<% if card.published? %>
|
<% if card.published? %>
|
||||||
<%= render partial: "cards/comments/comment", collection: card.comments.preloaded.chronologically, cached: true %>
|
<%= render partial: "cards/comments/comment", collection: card.comments.preloaded.chronologically, cached: true %>
|
||||||
<%= render "cards/comments/new", card: card %>
|
<% if Fizzy.saas? %>
|
||||||
|
<%= render "cards/comments/saas/new", card: card %>
|
||||||
|
<% else %>
|
||||||
|
<%= render "cards/comments/new", card: card %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= render "cards/comments/watchers", card: card %>
|
<%= render "cards/comments/watchers", card: card %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -13,5 +13,7 @@
|
|||||||
<span>Create and add another</span>
|
<span>Create and add another</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<%= render "cards/container/footer/saas/storage_limit_notice" if Fizzy.saas? %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -26,5 +26,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "cards/container/footer/create", card: card %>
|
<% if Fizzy.saas? %>
|
||||||
|
<%= render "cards/container/footer/saas/create", card: card %>
|
||||||
|
<% else %>
|
||||||
|
<%= render "cards/container/footer/create", card: card %>
|
||||||
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
module Card::StorageLimited
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
private
|
||||||
|
def ensure_within_storage_limit
|
||||||
|
head :forbidden if Current.account.exceeding_storage_limit? && !Current.identity.staff?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
module Card::StorageLimited::Commenting
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
include Card::StorageLimited
|
||||||
|
|
||||||
|
before_action :ensure_within_storage_limit, only: :create
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
module Card::StorageLimited::Creation
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
include Card::StorageLimited
|
||||||
|
|
||||||
|
before_action :ensure_within_storage_limit, only: :create, if: -> { request.format.json? }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
module Card::StorageLimited::Publishing
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
include Card::StorageLimited
|
||||||
|
|
||||||
|
before_action :ensure_within_storage_limit, only: :create
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
module Account::StorageLimited
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
STORAGE_LIMIT = 1.gigabyte
|
||||||
|
NEAR_STORAGE_LIMIT_THRESHOLD = 500.megabytes
|
||||||
|
|
||||||
|
def exceeding_storage_limit?
|
||||||
|
bytes_used > STORAGE_LIMIT
|
||||||
|
end
|
||||||
|
|
||||||
|
def nearing_storage_limit?
|
||||||
|
!exceeding_storage_limit? && bytes_used > STORAGE_LIMIT - NEAR_STORAGE_LIMIT_THRESHOLD
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<% if Current.account.exceeding_storage_limit? && !Current.identity.staff? %>
|
||||||
|
<%= render "cards/comments/saas/storage_limit_exceeded" %>
|
||||||
|
<% else %>
|
||||||
|
<%= render "cards/comments/new", card: card %>
|
||||||
|
<% end %>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<div class="comment-by-system comment-by-system--account-limit">
|
||||||
|
<div class="comment align-start full-width">
|
||||||
|
<div class="comment__content flex flex-column flex-item-grow full-width">
|
||||||
|
<div class="comment__body lexxy-content">
|
||||||
|
<div class="action-text-content">
|
||||||
|
<strong>This account has used all the included free storage (<%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %>).</strong>
|
||||||
|
<div><%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", target: "_blank" %> for unlimited storage.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<% if Current.account.exceeding_storage_limit? && !Current.identity.staff? %>
|
||||||
|
<%= render "cards/container/footer/saas/storage_limit_exceeded" %>
|
||||||
|
<% else %>
|
||||||
|
<%= render "cards/container/footer/create", card: card %>
|
||||||
|
<% end %>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<div class="card-perma__notch card-perma__notch--bottom">
|
||||||
|
<div class="card-perma__account-limit-message">
|
||||||
|
<strong>This account has used all the included free storage (<%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %>).</strong>
|
||||||
|
<div>
|
||||||
|
<%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", target: "_blank" %> for unlimited storage.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<% if Current.account.nearing_storage_limit? && !Current.identity.staff? %>
|
||||||
|
<div class="txt-small">
|
||||||
|
This account has used <strong><%= number_to_human_size(Current.account.bytes_used) %></strong> of <strong><%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %></strong> storage.
|
||||||
|
<div><%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", class: "txt-current txt-underline", target: "_blank" %> for unlimited storage.</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
@@ -133,10 +133,14 @@ module Fizzy
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.to_prepare do
|
config.to_prepare do
|
||||||
|
::Account.include Account::StorageLimited
|
||||||
::Identity.include Authorization::Identity, Identity::Devices
|
::Identity.include Authorization::Identity, Identity::Devices
|
||||||
::Session.include Session::Devices
|
::Session.include Session::Devices
|
||||||
::Signup.prepend Signup
|
::Signup.prepend Signup
|
||||||
ApplicationController.include Authorization::Controller
|
ApplicationController.include Authorization::Controller
|
||||||
|
CardsController.include(Card::StorageLimited::Creation)
|
||||||
|
Cards::CommentsController.include(Card::StorageLimited::Commenting)
|
||||||
|
Cards::PublishesController.include(Card::StorageLimited::Publishing)
|
||||||
|
|
||||||
Notification.register_push_target(:native)
|
Notification.register_push_target(:native)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Card::StorageLimited::CommentingTest < ActionDispatch::IntegrationTest
|
||||||
|
test "cannot create comments when storage limit exceeded" do
|
||||||
|
sign_in_as :david
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
Identity.any_instance.stubs(:staff?).returns(false)
|
||||||
|
|
||||||
|
assert_no_difference -> { Comment.count } do
|
||||||
|
post card_comments_path(cards(:logo), script_name: accounts(:"37s").slug),
|
||||||
|
params: { comment: { body: "Blocked comment" } },
|
||||||
|
as: :turbo_stream
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can create comments when under storage limit" do
|
||||||
|
sign_in_as :david
|
||||||
|
|
||||||
|
assert_difference -> { Comment.count } do
|
||||||
|
post card_comments_path(cards(:logo), script_name: accounts(:"37s").slug),
|
||||||
|
params: { comment: { body: "Allowed comment" } },
|
||||||
|
as: :turbo_stream
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "staff can create comments even when storage limit exceeded" do
|
||||||
|
sign_in_as :david
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
assert_difference -> { Comment.count } do
|
||||||
|
post card_comments_path(cards(:logo), script_name: accounts(:"37s").slug),
|
||||||
|
params: { comment: { body: "Staff comment" } },
|
||||||
|
as: :turbo_stream
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Card::StorageLimited::CreationTest < ActionDispatch::IntegrationTest
|
||||||
|
test "cannot create cards via JSON when storage limit exceeded" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
assert_no_difference -> { Card.count } do
|
||||||
|
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug),
|
||||||
|
params: { card: { title: "Blocked card" } },
|
||||||
|
as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can create cards via HTML when storage limit exceeded since they become drafts" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
accounts(:initech).update_column(:cards_count, 100)
|
||||||
|
boards(:miltons_wish_list).cards.drafted.where(creator: users(:mike)).destroy_all
|
||||||
|
|
||||||
|
assert_difference -> { Card.count } do
|
||||||
|
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :redirect
|
||||||
|
assert Card.last.drafted?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can create cards via JSON when under storage limit" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(500.megabytes)
|
||||||
|
accounts(:initech).update_column(:cards_count, 100)
|
||||||
|
|
||||||
|
assert_difference -> { Card.count } do
|
||||||
|
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug),
|
||||||
|
params: { card: { title: "Allowed card" } },
|
||||||
|
as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :created
|
||||||
|
end
|
||||||
|
|
||||||
|
test "staff can create cards via JSON even when storage limit exceeded" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
Identity.any_instance.stubs(:staff?).returns(true)
|
||||||
|
accounts(:initech).update_column(:cards_count, 100)
|
||||||
|
|
||||||
|
assert_difference -> { Card.count } do
|
||||||
|
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug),
|
||||||
|
params: { card: { title: "Staff card" } },
|
||||||
|
as: :json
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :created
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Card::StorageLimited::PublishingTest < ActionDispatch::IntegrationTest
|
||||||
|
test "cannot publish cards when storage limit exceeded" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
post card_publish_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
|
assert_response :forbidden
|
||||||
|
assert cards(:unfinished_thoughts).reload.drafted?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "can publish cards when under storage limit" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
post card_publish_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
|
assert_response :redirect
|
||||||
|
assert cards(:unfinished_thoughts).reload.published?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "staff can publish cards even when storage limit exceeded" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
Identity.any_instance.stubs(:staff?).returns(true)
|
||||||
|
|
||||||
|
post card_publish_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
|
assert_response :redirect
|
||||||
|
assert cards(:unfinished_thoughts).reload.published?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Card::StorageLimitedTest < ActionDispatch::IntegrationTest
|
||||||
|
test "draft card shows storage limit notice instead of create buttons when limit exceeded" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select ".card-perma__notch" do
|
||||||
|
assert_select "strong", text: /used all/
|
||||||
|
assert_select "a[href='https://github.com/basecamp/fizzy']", text: "Self-host Fizzy"
|
||||||
|
end
|
||||||
|
assert_select ".card-perma__notch-new-card-buttons", count: 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "draft card shows create buttons when under storage limit" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select ".card-perma__notch-new-card-buttons"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "staff sees create buttons even when storage limit exceeded" do
|
||||||
|
sign_in_as :mike
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
Identity.any_instance.stubs(:staff?).returns(true)
|
||||||
|
|
||||||
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select ".card-perma__notch-new-card-buttons"
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Comment::StorageLimitedTest < ActionDispatch::IntegrationTest
|
||||||
|
test "published card shows storage limit notice instead of comment form when limit exceeded" do
|
||||||
|
sign_in_as :david
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
Identity.any_instance.stubs(:staff?).returns(false)
|
||||||
|
|
||||||
|
get card_path(cards(:logo), script_name: accounts(:"37s").slug)
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select "strong", text: /used all/
|
||||||
|
assert_select "a[href='https://github.com/basecamp/fizzy']", text: "Self-host Fizzy"
|
||||||
|
assert_select "##{dom_id(cards(:logo), :new_comment)}", count: 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "published card shows comment form when under storage limit" do
|
||||||
|
sign_in_as :david
|
||||||
|
|
||||||
|
get card_path(cards(:logo), script_name: accounts(:"37s").slug)
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select "##{dom_id(cards(:logo), :new_comment)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "staff sees comment form even when storage limit exceeded" do
|
||||||
|
sign_in_as :david
|
||||||
|
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
get card_path(cards(:logo), script_name: accounts(:"37s").slug)
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select "##{dom_id(cards(:logo), :new_comment)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Account::StorageLimitedTest < ActiveSupport::TestCase
|
||||||
|
test "exceeding storage limit when bytes used exceeds 1 GB" do
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
assert accounts(:initech).exceeding_storage_limit?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not exceeding storage limit when bytes used equals 1 GB" do
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte)
|
||||||
|
|
||||||
|
assert_not accounts(:initech).exceeding_storage_limit?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not exceeding storage limit when under 1 GB" do
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(500.megabytes)
|
||||||
|
|
||||||
|
assert_not accounts(:initech).exceeding_storage_limit?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "nearing storage limit when within 500 MB of the limit" do
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(600.megabytes)
|
||||||
|
|
||||||
|
assert accounts(:initech).nearing_storage_limit?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not nearing storage limit when well under the threshold" do
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(400.megabytes)
|
||||||
|
|
||||||
|
assert_not accounts(:initech).nearing_storage_limit?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not nearing storage limit when already exceeding it" do
|
||||||
|
Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
|
||||||
|
|
||||||
|
assert_not accounts(:initech).nearing_storage_limit?
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user