From 1c4bf3e1e0c5e33d8540233d3069b9f68a06efdc Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Thu, 19 Mar 2026 15:36:36 +0100 Subject: [PATCH] Add per-account storage exceptions for SaaS (#2733) Allow granting specific accounts more storage via an account_storage_exceptions table in the saas DB, without relying on the blanket staff bypass. --- saas/app/models/account/storage_exception.rb | 5 ++ saas/app/models/account/storage_limited.rb | 22 +++++++-- .../saas/_storage_limit_exceeded.html.erb | 2 +- .../saas/_storage_limit_exceeded.html.erb | 2 +- .../saas/_storage_limit_notice.html.erb | 2 +- ...42914_create_account_storage_exceptions.rb | 10 ++++ saas/db/saas_schema.rb | 10 +++- .../models/account/storage_exception_test.rb | 46 +++++++++++++++++++ 8 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 saas/app/models/account/storage_exception.rb create mode 100644 saas/db/migrate/20260319142914_create_account_storage_exceptions.rb create mode 100644 saas/test/models/account/storage_exception_test.rb diff --git a/saas/app/models/account/storage_exception.rb b/saas/app/models/account/storage_exception.rb new file mode 100644 index 000000000..4c2db8955 --- /dev/null +++ b/saas/app/models/account/storage_exception.rb @@ -0,0 +1,5 @@ +class Account::StorageException < SaasRecord + belongs_to :account + + validates :bytes_allowed, presence: true, numericality: { greater_than: 0 } +end diff --git a/saas/app/models/account/storage_limited.rb b/saas/app/models/account/storage_limited.rb index dbab32b5d..46db55297 100644 --- a/saas/app/models/account/storage_limited.rb +++ b/saas/app/models/account/storage_limited.rb @@ -1,14 +1,30 @@ module Account::StorageLimited extend ActiveSupport::Concern - STORAGE_LIMIT = 1.gigabyte + DEFAULT_STORAGE_LIMIT = 1.gigabyte NEAR_STORAGE_LIMIT_THRESHOLD = 500.megabytes + included do + has_one :storage_exception + end + + def storage_limit + storage_exception&.bytes_allowed || DEFAULT_STORAGE_LIMIT + end + def exceeding_storage_limit? - bytes_used > STORAGE_LIMIT + bytes_used > storage_limit end def nearing_storage_limit? - !exceeding_storage_limit? && bytes_used > STORAGE_LIMIT - NEAR_STORAGE_LIMIT_THRESHOLD + !exceeding_storage_limit? && bytes_used > storage_limit - NEAR_STORAGE_LIMIT_THRESHOLD + end + + def add_storage_exception(bytes) + if storage_exception + storage_exception.update!(bytes_allowed: bytes) + else + create_storage_exception!(bytes_allowed: bytes) + end end end diff --git a/saas/app/views/cards/comments/saas/_storage_limit_exceeded.html.erb b/saas/app/views/cards/comments/saas/_storage_limit_exceeded.html.erb index 82ab7f25e..f5f68bda0 100644 --- a/saas/app/views/cards/comments/saas/_storage_limit_exceeded.html.erb +++ b/saas/app/views/cards/comments/saas/_storage_limit_exceeded.html.erb @@ -3,7 +3,7 @@
- This account has used all the included free storage (<%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %>). + This account has used all the included free storage (<%= number_to_human_size(Current.account.storage_limit) %>).
<%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", target: "_blank" %> for unlimited storage.
diff --git a/saas/app/views/cards/container/footer/saas/_storage_limit_exceeded.html.erb b/saas/app/views/cards/container/footer/saas/_storage_limit_exceeded.html.erb index ca0dc9be0..047b3b2c8 100644 --- a/saas/app/views/cards/container/footer/saas/_storage_limit_exceeded.html.erb +++ b/saas/app/views/cards/container/footer/saas/_storage_limit_exceeded.html.erb @@ -1,6 +1,6 @@