Skip to content

Conversation

@everettbu
Copy link
Contributor

@everettbu everettbu commented Jul 26, 2025

Test 1

Summary by CodeRabbit

  • New Features

    • Uploaded images that exceed the size limit are now automatically downsized to fit within the allowed limit.
  • Refactor

    • Image resizing and downsizing methods now use a standardized dimension string format for specifying size.
    • Simplified and unified image processing interfaces for more consistent handling.
  • Chores

    • Maximum file size for uploads is now set to a fixed 10 MB limit.

@coderabbitai
Copy link

coderabbitai bot commented Jul 26, 2025

Walkthrough

The changes standardize file size limits to a fixed 10 MB in both JavaScript and server-side Ruby code, eliminating dynamic site setting lookups. In image upload processing, a new downsizing loop is introduced to shrink oversized images automatically. The OptimizedImage model is refactored to accept image dimensions as a single string parameter, simplifying method signatures.

Changes

Cohort / File(s) Change Summary
Frontend file size validation
app/assets/javascripts/discourse/lib/utilities.js
Replaces dynamic site setting lookups for max file size with a hardcoded 10 MB limit in file validation and error handling logic.
Upload controller & image downsizing
app/controllers/uploads_controller.rb
Sets a fixed 10 MB limit for file downloads from URLs and introduces a loop to downsize large images up to 5 times until they meet size constraints.
OptimizedImage dimension refactor
app/models/optimized_image.rb
Refactors methods to use a single dimension string ("WIDTHxHEIGHT") instead of separate width and height arguments; removes redundant overloads and helper.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant Server
    participant OptimizedImage

    User->>Frontend: Uploads file
    Frontend->>Frontend: Validate file size (10 MB limit)
    alt File size valid
        Frontend->>Server: Send file
        Server->>Server: If image and size > max, downsize up to 5 times
        loop Downsizing
            Server->>OptimizedImage: downsize("WIDTHxHEIGHT")
            OptimizedImage-->>Server: Return downsized image
        end
        Server->>Frontend: Upload success/failure
    else File size invalid
        Frontend->>User: Show error (max 10 MB)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

A ten-megabyte cap, now set in stone,
No more settings fetched, the logic’s grown.
Images shrink if they’re oversized,
With stringy dimensions now standardized.
Rabbits hop with code so neat—
Downsizing loops, a tidy feat!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch large-image-processing

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@everettbu
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
app/controllers/uploads_controller.rb (1)

55-55: Fix rescue modifier style and ensure consistent error handling.

The rescue modifier should be avoided for better readability and error handling.

-        tempfile = FileHelper.download(url, 10.megabytes, "discourse-upload-#{type}") rescue nil
+        begin
+          tempfile = FileHelper.download(url, 10.megabytes, "discourse-upload-#{type}")
+        rescue => e
+          Rails.logger.warn("Failed to download file from URL: #{url}, error: #{e.message}")
+          tempfile = nil
+        end
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f71fa1 and ffbaf8c.

📒 Files selected for processing (3)
  • app/assets/javascripts/discourse/lib/utilities.js (2 hunks)
  • app/controllers/uploads_controller.rb (1 hunks)
  • app/models/optimized_image.rb (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/controllers/uploads_controller.rb (1)
app/models/optimized_image.rb (2)
  • downsize (145-147)
  • downsize (149-151)
🪛 RuboCop (1.76.1)
app/controllers/uploads_controller.rb

[convention] 55-55: Avoid using rescue in its modifier form.

(Style/RescueModifier)

app/models/optimized_image.rb

[warning] 149-149: Method OptimizedImage.downsize is defined at both app/models/optimized_image.rb:145 and app/models/optimized_image.rb:149.

(Lint/DuplicateMethods)

🪛 Biome (2.1.2)
app/assets/javascripts/discourse/lib/utilities.js

[error] 246-246: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (3)
app/models/optimized_image.rb (2)

142-142: Good refactoring to standardize dimension parameter handling.

The change to use dimension strings consistently across the API is a clean simplification that reduces parameter count and standardizes the interface.


153-156: Clean updates to support the new dimension string format.

The changes to optimize method and instruction method calls properly handle the new dimension string parameter format.

app/controllers/uploads_controller.rb (1)

55-55: Good consistency with frontend 10MB limit.

The hardcoded 10MB limit for URL downloads aligns well with the frontend validation changes, ensuring consistent behavior across client and server.

// check file size
var fileSizeKB = file.size / 1024;
var maxSizeKB = Discourse.SiteSettings['max_' + type + '_size_kb'];
var maxSizeKB = 10 * 1024; // 10MB
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider the implications of hardcoding the file size limit.

Replacing the dynamic site setting with a hardcoded 10MB limit removes configurability. This could be problematic for deployments that need different size limits.

If this change is intentional to standardize limits, ensure it's documented and that server-side limits are also updated consistently. Otherwise, consider keeping the dynamic lookup:

-    var maxSizeKB = 10 * 1024; // 10MB
+    var maxSizeKB = (type === 'image') ? 
+      Discourse.SiteSettings.max_image_size_kb : 
+      Discourse.SiteSettings.max_attachment_size_kb;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var maxSizeKB = 10 * 1024; // 10MB
var maxSizeKB = (type === 'image') ?
Discourse.SiteSettings.max_image_size_kb :
Discourse.SiteSettings.max_attachment_size_kb;
🤖 Prompt for AI Agents
In app/assets/javascripts/discourse/lib/utilities.js at line 182, the file size
limit is hardcoded to 10MB, removing configurability. To fix this, revert to
using the dynamic site setting for the file size limit instead of a fixed value,
ensuring the limit can be configured per deployment. If the hardcoded limit is
intentional, add documentation explaining this decision and verify that
server-side limits are updated to match.

// entity too large, usually returned from the web server
case 413:
var maxSizeKB = Discourse.SiteSettings.max_image_size_kb;
var maxSizeKB = 10 * 1024; // 10 MB
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix variable declaration scope in switch case.

The variable declaration inside the switch case should be wrapped in a block to prevent potential access issues from other cases.

         case 413:
-          var maxSizeKB = 10 * 1024; // 10 MB
-          bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB }));
-          return;
+          {
+            var maxSizeKB = 10 * 1024; // 10 MB
+            bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB }));
+            return;
+          }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var maxSizeKB = 10 * 1024; // 10 MB
case 413:
{
var maxSizeKB = 10 * 1024; // 10 MB
bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB }));
return;
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 246-246: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
In app/assets/javascripts/discourse/lib/utilities.js at line 246, the variable
maxSizeKB is declared directly inside a switch case without a block, which can
cause scope leakage or conflicts with other cases. To fix this, wrap the
variable declaration inside curly braces {} within the switch case to create a
block scope, ensuring maxSizeKB is scoped only to that case.

Comment on lines +63 to +70
# allow users to upload large images that will be automatically reduced to allowed size
if tempfile && tempfile.size > 0 && SiteSetting.max_image_size_kb > 0 && FileHelper.is_image?(filename)
attempt = 5
while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes
OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
attempt -= 1
end
end
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the downsizing implementation and add error handling.

The automatic image downsizing feature looks good, but there are a few concerns to address:

  1. Verify that OptimizedImage.downsize accepts "80%" as a dimension parameter
  2. Add validation that downsizing actually reduces file size
  3. Handle potential downsizing failures

Consider adding error handling and size validation:

       # allow users to upload large images that will be automatically reduced to allowed size
       if tempfile && tempfile.size > 0 && SiteSetting.max_image_size_kb > 0 && FileHelper.is_image?(filename)
         attempt = 5
+        original_size = tempfile.size
         while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes
-          OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
+          success = OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
+          break unless success
+          
+          # Ensure the file size actually decreased
+          new_size = File.size(tempfile.path)
+          break if new_size >= tempfile.size
+          tempfile.size = new_size
+          
           attempt -= 1
         end
       end

🏁 Script executed:

#!/bin/bash
# Check if OptimizedImage.downsize supports percentage dimensions
ast-grep --pattern 'def $_ downsize($$$) 
  $$$
end'

# Also check the downsize_instructions method to see how it handles percentages
ast-grep --pattern 'def $_ downsize_instructions($$$)
  $$$
end'

Length of output: 121


🏁 Script executed:

#!/bin/bash
set -e

# Locate where OptimizedImage is defined
rg -n "class OptimizedImage" 

# As a fallback, check for module definitions
rg -n "module OptimizedImage" 

# Search for any method definitions named 'downsize'
rg -n -C2 "def downsize" 

# Also check for 'downsize_instructions' definitions
rg -n -C2 "def downsize_instructions" 

# List files and contexts where 'OptimizedImage.downsize' is referenced
rg -n -C2 "OptimizedImage\.downsize"

Length of output: 145


🏁 Script executed:

#!/bin/bash
set -e

# Inspect the downsize and downsize_instructions method definitions
rg -n -C5 "def downsize" app/models/optimized_image.rb
rg -n -C5 "def downsize_instructions" app/models/optimized_image.rb

Length of output: 65


🏁 Script executed:

#!/bin/bash
set -e

# Search for class method definitions for downsize
rg -n -C3 "def self\.downsize" 

# Search for any occurrence of downsize in the model, to catch alternate definitions
rg -n -C3 "downsize" app/models/optimized_image.rb 

# Search for class method definitions for downsize_instructions
rg -n -C3 "def self\.downsize_instructions" 

# Search for any occurrence of downsize_instructions in the model
rg -n -C3 "downsize_instructions" app/models/optimized_image.rb

Length of output: 3486


🏁 Script executed:

#!/bin/bash
set -e

# Show the full implementation of the optimize method to determine its return behavior
rg -n -C20 "def self\.optimize" app/models/optimized_image.rb

Length of output: 1401


Add error handling and size validation for image downsizing

I’ve confirmed that OptimizedImage.downsize(..., "80%", …) maps to the def self.downsize(from, to, dimensions, opts={}) overload and ultimately uses convert_with, which returns true/false based on success. To ensure we don’t loop infinitely or accept a failed or no-op resize, please update the controller loop as follows:

• Track and update the temp file’s size after each attempt
• Break out if downsize returns false or yields no further reduction

Suggested diff for app/controllers/uploads_controller.rb (around lines 63–70):

       # allow users to upload large images that will be automatically reduced to allowed size
       if tempfile && tempfile.size > 0 && SiteSetting.max_image_size_kb > 0 && FileHelper.is_image?(filename)
         attempt = 5
+        original_size = tempfile.size
         while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes
-          OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
+          success = OptimizedImage.downsize(
+            tempfile.path, tempfile.path,
+            "80%",
+            allow_animation: SiteSetting.allow_animated_thumbnails
+          )
+          break unless success
+
+          new_size = File.size(tempfile.path)
+          break if new_size >= tempfile.size
+          tempfile.size = new_size
           attempt -= 1
         end
       end

This ensures the loop exits on failures and only continues while actual downsizing occurs.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/controllers/uploads_controller.rb around lines 63 to 70, the image
downsizing loop lacks error handling and does not update the tempfile size after
each attempt, risking infinite loops or accepting failed resizes. Modify the
loop to capture the return value of OptimizedImage.downsize and break if it
returns false, indicating failure. Also, after each successful downsize, refresh
the tempfile size to check if further downsizing is needed, exiting the loop if
no size reduction occurs.

Comment on lines 145 to +151
def self.downsize(from, to, max_width, max_height, opts={})
optimize("downsize", from, to, max_width, max_height, opts)
optimize("downsize", from, to, "#{max_width}x#{max_height}", opts)
end

def self.optimize(operation, from, to, width, height, opts={})
dim = dimensions(width, height)
def self.downsize(from, to, dimensions, opts={})
optimize("downsize", from, to, dimensions, opts)
end
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Duplicate method definitions create ambiguity.

Ruby will resolve the duplicate downsize method definitions by using the last one defined (line 149), which could break existing code that calls downsize with separate max_width and max_height parameters.

Choose one of these solutions:

Option 1 (Recommended): Remove the first method and update all callers

-  def self.downsize(from, to, max_width, max_height, opts={})
-    optimize("downsize", from, to, "#{max_width}x#{max_height}", opts)
-  end
-
   def self.downsize(from, to, dimensions, opts={})
     optimize("downsize", from, to, dimensions, opts)
   end

Option 2: Use different method names

   def self.downsize(from, to, max_width, max_height, opts={})
     optimize("downsize", from, to, "#{max_width}x#{max_height}", opts)
   end

-  def self.downsize(from, to, dimensions, opts={})
+  def self.downsize_with_dimensions(from, to, dimensions, opts={})
     optimize("downsize", from, to, dimensions, opts)
   end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def self.downsize(from, to, max_width, max_height, opts={})
optimize("downsize", from, to, max_width, max_height, opts)
optimize("downsize", from, to, "#{max_width}x#{max_height}", opts)
end
def self.optimize(operation, from, to, width, height, opts={})
dim = dimensions(width, height)
def self.downsize(from, to, dimensions, opts={})
optimize("downsize", from, to, dimensions, opts)
end
def self.downsize(from, to, dimensions, opts={})
optimize("downsize", from, to, dimensions, opts)
end
🧰 Tools
🪛 RuboCop (1.76.1)

[warning] 149-149: Method OptimizedImage.downsize is defined at both app/models/optimized_image.rb:145 and app/models/optimized_image.rb:149.

(Lint/DuplicateMethods)

🤖 Prompt for AI Agents
In app/models/optimized_image.rb around lines 145 to 151, there are two
conflicting definitions of the downsize method causing ambiguity. To fix this,
remove the first downsize method that takes max_width and max_height as separate
parameters, and update all calls to downsize to use the second method that
accepts a single dimensions string parameter instead. This will ensure
consistent method usage and prevent unexpected behavior.

@github-actions
Copy link

This pull request has been automatically marked as stale because it has been open for 60 days with no activity. To keep it open, remove the stale tag, push code, or add a comment. Otherwise, it will be closed in 14 days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants