Skip to content

Conversation

@ricardogarim
Copy link
Contributor

@ricardogarim ricardogarim commented Jan 6, 2026

Proposed changes (including videos or screenshots)

Refactors the file upload pipeline to use streams instead of buffers, fixing memory issues that caused app crashes during large file uploads.

Key changes:

  • Files are now streamed directly to disk during multipart parsing instead of being buffered in memory
  • Removed unnecessary buffering throughout the upload pipeline (validation, EXIF stripping, file writing)
  • New MultipartUploadHandler class centralizes stream-based multipart handling
  • _doInsert now accepts file paths, avoiding re-reading files into memory after they've been written to temp storage

Issue(s)

Further comments

The previous implementation buffered entire files in memory at multiple stages (parsing, validation, processing), causing memory exhaustion on large uploads. This refactoring ensures files flow through streams to disk, significantly reducing memory footprint.

Benchmark and implementation discussions: open.rocket.chat

Summary by CodeRabbit

  • New Features

    • Streamed multipart upload handler with parsing utility, optional EXIF stripping and transforms; livechat upload preserves descriptions.
  • Bug Fixes

    • Content-type aware request parsing to avoid consuming multipart bodies; clearer upload error types and stricter size/type checks.
    • More reliable temp-file cleanup and write-failure handling.
  • Refactor

    • Centralized upload pipeline now accepts buffers, file paths, and streams; insertion and post-processing streamlined.
  • Tests

    • Updated unit and e2e tests to cover new upload flows and related settings.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 6, 2026

🦋 Changeset detected

Latest commit: 0b8781e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 40 packages
Name Type
@rocket.chat/http-router Patch
@rocket.chat/meteor Patch
@rocket.chat/core-services Patch
@rocket.chat/federation-matrix Patch
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/abac Patch
@rocket.chat/network-broker Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/core-typings Patch
@rocket.chat/rest-typings Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/apps Patch
@rocket.chat/cron Patch
@rocket.chat/ddp-client Patch
@rocket.chat/fuselage-ui-kit Patch
@rocket.chat/gazzodown Patch
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-avatar Patch
@rocket.chat/ui-client Patch
@rocket.chat/ui-contexts Patch
@rocket.chat/ui-voip Patch
@rocket.chat/web-ui-registration Patch
@rocket.chat/license Patch
@rocket.chat/media-calls Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/models Patch
@rocket.chat/mock-providers Patch
@rocket.chat/ui-video-conf Patch
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 6, 2026

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a Busboy-based MultipartUploadHandler that streams uploads to temp files, threads the raw IncomingMessage into API action contexts, and updates router, middleware, file upload core, UFS, endpoints, apps bridge, and tests to operate on temp-file paths (string) or streams instead of buffering file contents in memory.

Changes

Cohort / File(s) Summary
API Context & Middleware
apps/meteor/app/api/server/definition.ts, apps/meteor/app/api/server/router.ts, apps/meteor/app/api/server/middlewares/logger.ts
Add IncomingMessage typing and incoming to action contexts; logger/router stop auto-parsing multipart/form-data and preserve raw incoming request in env.
Multipart Upload Handler (new)
apps/meteor/app/api/server/lib/MultipartUploadHandler.ts
New Busboy-based handler with ParsedUpload/ParseOptions, parseRequest, EXIF-strip transform, cleanup, limits, MIME checks; writes file to temp paths and returns { file, fields }.
Endpoints using uploads
apps/meteor/app/api/server/v1/rooms.ts, apps/meteor/app/livechat/imports/server/rest/upload.ts, apps/meteor/tests/end-to-end/api/rooms.ts
Replace getUploadFormData with MultipartUploadHandler.parseRequest; use file.tempFilePath/file.size; adjust error handling and tests for missing/multiple files.
FileUpload core & Apps bridge
apps/meteor/app/file-upload/server/lib/FileUpload.ts, apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts, apps/meteor/app/apps/server/bridges/listeners.ts
Widen content types to accept `Buffer
UFS / File store & methods
apps/meteor/server/ufs/ufs-store.ts, apps/meteor/server/ufs/ufs-methods.ts, apps/meteor/server/ufs/ufs-local.spec.ts
Switch temp-file deletions to fs.promises.unlink with ENOENT handling; remove read-stream-based size recalculation in finalization; tests updated for expected unlink calls.
HTTP Router & Tests
packages/http-router/src/Router.ts, packages/http-router/src/Router.spec.ts
parseBodyParams early-returns for multipart/form-data (no auto-parse); tests updated to check c.env.incoming and raw request handling.
Startup, Filters & Tests
apps/meteor/server/startup/initialData.ts, apps/meteor/tests/e2e/image-upload.spec.ts, apps/meteor/server/ufs/ufs-filter.ts
Insert raw buffers directly into store (no buffer->stream conversion); tests manage separate rotate/EXIF settings; widen filter hook signatures to accept `Buffer
Changeset / Changelog
.changeset/twelve-sheep-accept.md
Add changeset noting file-upload flow improvement to avoid buffering in memory.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Logger as Logger
    participant Router as Router
    participant Handler as Handler
    participant MUH as MultipartUploadHandler
    participant Busboy as Busboy
    participant FS as FS
    participant Store as Store

    Client->>Logger: POST /upload (multipart/form-data)
    Logger->>Router: forward request (no multipart parsing)
    Router->>Handler: invoke handler with c.env.incoming
    Handler->>MUH: parseRequest(incoming, options)
    MUH->>Busboy: feed request stream
    Busboy->>FS: stream file -> temp path (apply transforms)
    FS-->>MUH: temp file written (size recorded)
    MUH-->>Handler: return { file: ParsedUpload, fields }
    Handler->>Store: insert(fileData, file.tempFilePath)
    Store->>FS: finalize and cleanup temp file
    Store-->>Handler: upload result
    Handler-->>Client: 200 OK / upload details
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ggazzo
  • KevLehman

Poem

🐰 I hop through filestreams, temp paths in my paw,
I carry IncomingMessage right into the maw,
I pipe, strip EXIF, and leave no bulky heap,
From client to store — a tidy, sleepy sweep,
Rabbit hums — uploads now quiet, swift, and neat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main objective: preventing file content buffering in user uploads, which is the core refactoring focus.
Linked Issues check ✅ Passed All code changes comprehensively address SUP-912 objectives: MultipartUploadHandler streams files to disk [multiple files], _doInsert accepts file paths avoiding memory re-reads [FileUpload.ts], and unnecessary buffering is removed across validation and EXIF stages.
Out of Scope Changes check ✅ Passed All changes are directly related to the streaming upload refactor: infrastructure updates (router.ts, definition.ts), stream-based multipart parsing (MultipartUploadHandler.ts), EXIF handling integration (logger.ts, FileUpload.ts), file store updates (ufs-store.ts, ufs-filter.ts), and related endpoint migrations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/s3-large-uploads

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Jan 6, 2026

Codecov Report

❌ Patch coverage is 45.45455% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.60%. Comparing base (ecf52b6) to head (0b8781e).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #38071      +/-   ##
===========================================
- Coverage    70.64%   70.60%   -0.05%     
===========================================
  Files         3133     3133              
  Lines       108373   108382       +9     
  Branches     19492    19521      +29     
===========================================
- Hits         76564    76518      -46     
- Misses       29813    29872      +59     
+ Partials      1996     1992       -4     
Flag Coverage Δ
e2e 60.30% <ø> (-0.02%) ⬇️
e2e-api 47.17% <55.55%> (-1.18%) ⬇️
unit 71.68% <38.70%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

📦 Docker Image Size Report

➡️ Changes

Service Current Baseline Change Percent
sum of all images 0B 0B 0B
account-service 0B 0B 0B
authorization-service 0B 0B 0B
ddp-streamer-service 0B 0B 0B
omnichannel-transcript-service 0B 0B 0B
presence-service 0B 0B 0B
queue-worker-service 0B 0B 0B
rocketchat 0B 0B 0B

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/24 19:38", "12/25 17:51", "12/26 13:18", "12/29 19:01", "12/30 20:52", "01/16 18:56 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.00]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "queue-worker-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "rocketchat" [0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.00]
Loading

Statistics (last 30 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.4GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 0B
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-38071
  • Baseline: develop
  • Timestamp: 2026-01-16 18:56:29 UTC
  • Historical data points: 30

Updated: Fri, 16 Jan 2026 18:56:30 GMT

@ricardogarim ricardogarim force-pushed the refactor/s3-large-uploads branch 3 times, most recently from bb808eb to 301d878 Compare January 8, 2026 12:06
@ricardogarim ricardogarim force-pushed the refactor/s3-large-uploads branch 2 times, most recently from b287bf2 to 9a488cc Compare January 8, 2026 22:22
@d-gubert d-gubert force-pushed the refactor/s3-large-uploads branch 3 times, most recently from 9f30d70 to e408d2d Compare January 10, 2026 12:55
@d-gubert d-gubert added this to the 8.1.0 milestone Jan 11, 2026
@d-gubert d-gubert force-pushed the refactor/s3-large-uploads branch from 37cce45 to 8ddd8a2 Compare January 12, 2026 19:37
@d-gubert
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

✅ 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
Contributor

@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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
apps/meteor/server/ufs/ufs-methods.ts (1)

72-76: Potential temp file leak in error path.

When an error occurs before store.write is invoked (e.g., findOne fails, validate fails, or createReadStream throws), the catch block removes the file from the store but doesn't clean up the temp file. This could leave orphaned temp files on disk.

Consider adding temp file cleanup in the catch block:

🐛 Proposed fix
 		} catch (err: any) {
 			// If write failed, remove the file
 			await store.removeById(fileId, { session: options?.session });
+			void removeTempFile();
 			reject(new Meteor.Error('ufs: cannot upload file', err));
 		}
apps/meteor/ee/server/apps/communication/rest.ts (2)

331-348: Missing temp file cleanup after reading.

After reading the uploaded file with readFile(file.tempFilePath), the temporary file is not cleaned up. This could accumulate disk space over time, especially for large app packages.

Proposed fix
 						if (!file) {
 							return API.v1.failure({ error: 'No file was uploaded' });
 						}

 						buff = await readFile(file.tempFilePath);
+						await UploadService.cleanup(file.tempFilePath);
 						permissionsGranted = (() => {

826-843: Missing temp file cleanup in update route.

Same issue as the install route - the temporary file at file.tempFilePath is not cleaned up after reading.

Proposed fix
 						if (!file) {
 							return API.v1.failure({ error: 'No file was uploaded' });
 						}

 						buff = await readFile(file.tempFilePath);
+						await UploadService.cleanup(file.tempFilePath);
 						permissionsGranted = (() => {
apps/meteor/app/api/server/v1/emoji-custom.ts (1)

118-141: Missing temp file cleanup in create route.

After reading the emoji file buffer, the temporary file should be cleaned up. Consider adding cleanup in both success and error paths.

Proposed fix with try/finally for cleanup
 			if (!file) {
 				throw new Meteor.Error('error-no-file-uploaded', 'No file was uploaded');
 			}

-			const fileBuffer = await readFile(file.tempFilePath);
+			let fileBuffer: Buffer;
+			try {
+				fileBuffer = await readFile(file.tempFilePath);
+			} finally {
+				await UploadService.cleanup(file.tempFilePath);
+			}

 			const isUploadable = await Media.isImage(fileBuffer);
apps/meteor/app/api/server/v1/users.ts (1)

275-306: Missing temp file cleanup after successful avatar upload.

After calling setUserAvatar, the temp file at file.tempFilePath is never cleaned up. This could lead to disk space exhaustion over time.

Proposed fix
-			await setUserAvatar(user, await readFile(file.tempFilePath), file.mimetype, 'rest');
+			try {
+				await setUserAvatar(user, await readFile(file.tempFilePath), file.mimetype, 'rest');
+			} finally {
+				await UploadService.cleanup(file.tempFilePath);
+			}
🤖 Fix all issues with AI agents
In @apps/meteor/app/api/server/lib/UploadService.ts:
- Around line 176-178: The filesLimit event handler in UploadService currently
rejects with a plain string ('Just 1 file is allowed'), causing inconsistent
error types versus other limit events; change the rejection to throw/reject a
Meteor.Error with a matching error code/message (e.g., new
Meteor.Error('files-limit', 'Just 1 file is allowed')) in the
bb.on('filesLimit', ...) handler so it matches the other limit event handlers
like the ones that use Meteor.Error.
- Around line 129-136: The transform streams in UploadService.ts (the loop that
reassigns currentStream using options.transforms and the final
currentStream.pipe(writeStream)) do not attach 'error' handlers, so transform
failures may never reject the upload promise; add 'error' listeners on each
transform instance as you iterate (and also on the initial file stream and
writeStream) that forward the error to the upload's reject callback or a shared
error handler, unpipe/destroy streams on error to prevent resource leaks, and
ensure the same handler only runs once (e.g., by using a boolean flag or
once-wrapped callback) so any transform or writeStream error reliably aborts the
operation and rejects the promise.

In @apps/meteor/app/api/server/v1/assets.ts:
- Around line 40-41: The temp file at file.tempFilePath is never removed after
reading; update the code around the readFile and
RocketChatAssets.setAssetWithBuffer call to ensure the temp file is deleted
(fs.unlink/await unlink) in a finally block so it runs on success or error—use
file.tempFilePath as the target for removal and keep the existing use of
fileBuffer and RocketChatAssets.setAssetWithBuffer to set the asset.
- Around line 26-28: The endpoint in apps/meteor/app/api/server/v1/assets.ts
currently throws a raw Error("No file was uploaded") which is inconsistent with
other endpoints; replace that raw throw with the project-standard error handling
(either call API.v1.failure('No file was uploaded') to return a proper API
response or throw a Meteor.Error with an appropriate error id/message) so the
client receives a structured 400-style failure; locate the check for file
presence in the upload handler (the block containing if (!file) { ... }) and
swap the throw for the consistent API.v1.failure(...) or new Meteor.Error(...)
usage used elsewhere.

In @apps/meteor/app/api/server/v1/emoji-custom.ts:
- Around line 184-198: The update route reads an uploaded temp file but never
deletes it, leaving orphaned temp files; wrap the file handling in a try/finally
(or ensure a finally branch) around the
readFile/insertOrUpdateEmoji/uploadEmojiCustomWithBuffer sequence and call the
async unlink on file.tempFilePath to remove the temporary file whether the
operation succeeds or fails; update the code paths that use isNewFile,
file.tempFilePath, readFile, insertOrUpdateEmoji, and
uploadEmojiCustomWithBuffer to perform the cleanup, and import/use
fs.promises.unlink (or equivalent) to delete the temp file.

In @apps/meteor/app/file-upload/server/lib/FileUpload.ts:
- Around line 391-419: The code caches the setting into shouldStripExif but
later re-reads settings.get('Message_Attachments_Strip_Exif') when deciding to
strip metadata; change that conditional to use the cached shouldStripExif
variable instead (in the branch after rotated check), i.e., replace the direct
settings.get call with shouldStripExif so
UploadService.stripExifFromFile(tmpFile) is invoked consistently based on the
previously read shouldStripExif value.

In @apps/meteor/server/ufs/ufs-store.ts:
- Around line 168-171: The finishHandler currently returns early when
file.complete is true without invoking the caller callback, which can leave the
writer waiting; update finishHandler so that when file.complete is true it calls
the existing callback with the existing file (e.g., callback(null, file) or the
appropriate success signature used elsewhere) before returning, and ensure you
preserve the callback-once semantics (don’t call it twice) by checking any local
"done" / "called" flag or reusing the same guard used elsewhere in this
function.
🧹 Nitpick comments (1)
apps/meteor/app/api/server/lib/UploadService.ts (1)

68-68: Consider typing the limits object.

Using any bypasses type safety. Busboy accepts a typed limits object.

Suggested typing
-		const limits: any = { files: 1 };
+		const limits: busboy.Limits = { files: 1 };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3e54a03 and 8ddd8a2.

📒 Files selected for processing (19)
  • apps/meteor/app/api/server/definition.ts
  • apps/meteor/app/api/server/lib/UploadService.ts
  • apps/meteor/app/api/server/middlewares/logger.ts
  • apps/meteor/app/api/server/router.ts
  • apps/meteor/app/api/server/v1/assets.ts
  • apps/meteor/app/api/server/v1/emoji-custom.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
  • apps/meteor/app/livechat/imports/server/rest/upload.ts
  • apps/meteor/ee/server/apps/communication/rest.ts
  • apps/meteor/server/startup/initialData.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/server/ufs/ufs-methods.ts
  • apps/meteor/server/ufs/ufs-store.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
  • packages/http-router/src/Router.spec.ts
  • packages/http-router/src/Router.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/ufs/ufs-methods.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/app/api/server/definition.ts
  • apps/meteor/app/api/server/middlewares/logger.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • packages/http-router/src/Router.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/app/api/server/lib/UploadService.ts
  • apps/meteor/server/startup/initialData.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/ee/server/apps/communication/rest.ts
  • apps/meteor/app/api/server/v1/emoji-custom.ts
  • packages/http-router/src/Router.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
  • apps/meteor/app/api/server/router.ts
  • apps/meteor/app/livechat/imports/server/rest/upload.ts
  • apps/meteor/server/ufs/ufs-store.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/api/server/v1/assets.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • packages/http-router/src/Router.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
apps/meteor/tests/e2e/**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

apps/meteor/tests/e2e/**/*.spec.ts: All test files must be created in apps/meteor/tests/e2e/ directory
Avoid using page.locator() in Playwright tests - always prefer semantic locators such as page.getByRole(), page.getByLabel(), page.getByText(), or page.getByTitle()
Use test.beforeAll() and test.afterAll() for setup/teardown in Playwright tests
Use test.step() for complex test scenarios to improve organization in Playwright tests
Group related tests in the same file
Utilize Playwright fixtures (test, page, expect) for consistency in test files
Prefer web-first assertions (toBeVisible, toHaveText, etc.) in Playwright tests
Use expect matchers for assertions (toEqual, toContain, toBeTruthy, toHaveLength, etc.) instead of assert statements in Playwright tests
Use page.waitFor() with specific conditions instead of hardcoded timeouts in Playwright tests
Implement proper wait strategies for dynamic content in Playwright tests
Maintain test isolation between test cases in Playwright tests
Ensure clean state for each test execution in Playwright tests
Ensure tests run reliably in parallel without shared state conflicts

Files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
apps/meteor/tests/e2e/**/*.{ts,spec.ts}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

apps/meteor/tests/e2e/**/*.{ts,spec.ts}: Store commonly used locators in variables/constants for reuse
Follow Page Object Model pattern consistently in Playwright tests

Files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
🧠 Learnings (20)
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/server/ufs/ufs-methods.ts
  • apps/meteor/server/ufs/ufs-store.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • packages/http-router/src/Router.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure clean state for each test execution in Playwright tests

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Maintain test isolation between test cases in Playwright tests

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.beforeAll()` and `test.afterAll()` for setup/teardown in Playwright tests

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.{ts,spec.ts} : Store commonly used locators in variables/constants for reuse

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.step()` for complex test scenarios to improve organization in Playwright tests

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/ee/server/apps/communication/rest.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-09-15T13:10:30.049Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: packages/http-router/src/Router.ts:416-425
Timestamp: 2025-09-15T13:10:30.049Z
Learning: In packages/http-router/src/Router.ts, the dispatch() method's use of replaceAll('//', '/') on the full path is acceptable because URL normalization and query string handling is performed by the caller function before dispatch() is invoked.

Applied to files:

  • packages/http-router/src/Router.spec.ts
  • packages/http-router/src/Router.ts
📚 Learning: 2025-12-10T21:00:43.645Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:43.645Z
Learning: Adopt the monorepo-wide Jest testMatch pattern: <rootDir>/src/**/*.spec.{ts,js,mjs} (represented here as '**/src/**/*.spec.{ts,js,mjs}') to ensure spec files under any package's src directory are picked up consistently across all packages in the Rocket.Chat monorepo. Apply this pattern in jest.config.ts for all relevant packages to maintain uniform test discovery.

Applied to files:

  • packages/http-router/src/Router.spec.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/server/startup/initialData.ts
  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/server/startup/initialData.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
📚 Learning: 2025-11-05T21:04:35.787Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37357
File: ee/packages/federation-matrix/src/setup.ts:103-120
Timestamp: 2025-11-05T21:04:35.787Z
Learning: In Rocket.Chat's federation-matrix setup (ee/packages/federation-matrix/src/setup.ts and apps/meteor/ee/server/startup/federation.ts), configureFederationMatrixSettings does not need to be called before setupFederationMatrix. The SDK's init() establishes infrastructure (database, event handlers, APIs) first, and the configuration can be applied later via settings watchers before actual federation events are processed. The config only matters when events actually occur, at which point all infrastructure is already configured.

Applied to files:

  • apps/meteor/app/api/server/v1/rooms.ts
🧬 Code graph analysis (8)
apps/meteor/app/api/server/lib/UploadService.ts (1)
packages/core-services/src/index.ts (1)
  • MeteorError (54-54)
apps/meteor/app/api/server/v1/users.ts (1)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
apps/meteor/ee/server/apps/communication/rest.ts (1)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
apps/meteor/app/api/server/v1/emoji-custom.ts (2)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
packages/core-services/src/index.ts (1)
  • Media (163-163)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (2)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
apps/meteor/server/ufs/ufs-methods.ts (1)
  • ufsComplete (10-78)
apps/meteor/app/livechat/imports/server/rest/upload.ts (2)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)
  • FileUpload (113-694)
apps/meteor/app/api/server/v1/rooms.ts (1)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
apps/meteor/app/api/server/v1/assets.ts (1)
apps/meteor/app/api/server/lib/UploadService.ts (1)
  • UploadService (31-201)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (60)
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
  • GitHub Check: 🚢 Build Docker (amd64, account-service, presence-service, omnichannel-transcript-service, cove...
  • GitHub Check: 🚢 Build Docker (arm64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, rocketchat, coverage)
  • GitHub Check: 🚢 Build Docker (amd64, authorization-service, queue-worker-service, ddp-streamer-service, cove...
🔇 Additional comments (21)
apps/meteor/server/ufs/ufs-local.spec.ts (1)

99-104: LGTM!

The test correctly reflects the updated behavior where unlink is now called twice (for both temp and final files). The inline comment provides good clarity on the expected behavior.

apps/meteor/server/ufs/ufs-methods.ts (1)

22-25: LGTM!

The simplified Promise-based cleanup is a good improvement. Catching errors and logging a warning is appropriate for cleanup operations—it ensures the upload flow isn't interrupted by cleanup failures while still providing visibility for debugging.

apps/meteor/server/startup/initialData.ts (1)

157-157: LGTM! Simplifies avatar upload by eliminating unnecessary stream conversion.

This change removes the intermediate bufferToStream() step, passing the buffer directly to insert(). The insert method signature on the FileUpload store explicitly accepts streamOrBuffer: stream.Readable | Buffer | string, so passing the Buffer directly is correct. This aligns with the PR's goal of streamlining upload handling and reducing resource overhead.

apps/meteor/server/ufs/ufs-store.ts (2)

173-213: LGTM — proper error handling for async finalization.

Wrapping the finalization logic in try/catch ensures that any exceptions during file completion are captured and propagated through the callback rather than being silently swallowed or causing unhandled rejections.


234-236: LGTM — idempotent cleanup with proper ENOENT handling.

Using fs.promises.unlink with a catch that ignores ENOENT is the correct pattern for cleanup operations: if the temp file is already gone, that's the desired state.

apps/meteor/tests/e2e/image-upload.spec.ts (2)

9-16: LGTM - Proper test setup for new execution order.

The test correctly saves both Message_Attachments_Strip_Exif and FileUpload_RotateImages settings in beforeAll and restores them in afterAll, maintaining proper test isolation.


47-62: LGTM - Clear test scenario with helpful comment.

The comment at lines 50-51 appropriately documents the dependency between image rotation and EXIF stripping order, ensuring the test properly validates EXIF stripping behavior in isolation.

apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts (1)

34-34: LGTM - UploadService stub added.

The stub for UploadService follows the existing proxyquire pattern used throughout this test file, correctly isolating the FileUpload module from the new dependency.

packages/http-router/src/Router.spec.ts (1)

449-486: Test correctly validates multipart handling without auto-parsing.

The test properly verifies that:

  1. Multipart/form-data is not auto-parsed by the router
  2. Raw request is available via c.env.incoming for manual parsing

The c.env.incoming is populated by the honoAdapterForExpress middleware, which passes the Express request object as the Hono context's env variable. The test will work as written.

packages/http-router/src/Router.ts (1)

163-166: LGTM - Multipart parsing delegation to UploadService.

This change correctly avoids consuming the request body stream at the router level for multipart requests, enabling streaming-based handling via UploadService.parse(). The empty object fallback is appropriate since multipart routes will parse the body manually.

The pattern is that multipart upload routes do not define body validators—they handle their own validation after parsing with UploadService.parse(). This design avoids the theoretical issue of validation failing on an empty body object.

apps/meteor/app/api/server/v1/rooms.ts (1)

200-235: LGTM - Proper use of UploadService with fileStore.

The implementation correctly uses this.incoming (IncomingMessage) for UploadService.parse and passes file.tempFilePath to fileStore.insert. This approach avoids buffering the entire file in memory. The fileStore.insert method handles the temporary file by renaming it to the UFS temp directory, then ufsComplete is called which pipes the file to the storage backend and cleans up the temp file via fs.promises.unlink upon completion.

apps/meteor/app/api/server/definition.ts (1)

1-2: LGTM - Type extension for IncomingMessage access.

The addition of incoming: IncomingMessage to ActionThis properly exposes the raw Node.js request needed by UploadService.parse. This is a clean type-level change supporting the streaming upload refactor.

apps/meteor/app/livechat/imports/server/rest/upload.ts (2)

36-41: LGTM - Size limit handling is correct.

The conditional maxFileSize > -1 ? maxFileSize : undefined correctly disables the size limit when maxFileSize is -1 (meaning no limit), allowing UploadService.parse to skip file size enforcement.


43-73: LGTM - File validation and insertion flow.

The migration to UploadService.parse is well-implemented:

  • Properly validates file presence and MIME type
  • Correctly maps parsed file properties to the details object
  • Passes file.tempFilePath to fileStore.insert, which aligns with the updated _doInsert method that handles string paths via rename
apps/meteor/app/api/server/router.ts (1)

9-27: LGTM - Clean type extension for raw request access.

The HonoContext type and APIActionContext extension correctly expose the Node.js IncomingMessage through Hono's context bindings. This enables UploadService.parse to work with both the Fetch API Request and the raw Node.js stream.

apps/meteor/app/api/server/lib/UploadService.ts (3)

114-117: Missing temp file cleanup on field mismatch rejection.

When the field name doesn't match, the stream is resumed but the file event handler returns after rejecting. If a temp file was partially created, it won't be cleaned up.

However, looking more closely, at this point tempFilePath hasn't been assigned yet (it's assigned after this check at line 125), so this is actually fine.


31-62: LGTM - Cleanup and EXIF stripping utilities.

The cleanup method properly handles errors gracefully with a warning. The stripExifFromFile method correctly uses a temporary .stripped file to avoid data loss if the transform fails, and cleans up on error.


91-94: Confirm TypeScript target compatibility with Promise.withResolvers.

Promise.withResolvers is available in Node.js 22.0.0+; the project requires Node.js 22.16.0, so runtime support is present. However, TypeScript is configured with target es2018 (in apps/meteor/tsconfig.json), which predates ES2024 when Promise.withResolvers was introduced. This may cause TypeScript compilation to fail or produce type errors unless the TypeScript library definitions include it. Verify that the TypeScript lib includes Promise.withResolvers or adjust the target/lib configuration accordingly.

apps/meteor/app/file-upload/server/lib/FileUpload.ts (3)

805-831: LGTM - Validation flow with content type handling.

The _validateFile method properly:

  • Returns early if no filter check exists
  • Converts Readable streams to buffers for validation (necessary since validation may need full content)
  • Handles Uint8Array to Buffer conversion for services compatibility
  • Passes undefined for string paths since file content validation requires buffer

833-856: LGTM - Flexible content handling in _doInsert.

The updated _doInsert correctly handles:

  • string: Treated as a file path, renamed to temp location
  • ArrayBufferView (Buffer/Uint8Array): Written directly to temp file
  • Readable: Piped to temp file with proper stream completion via finished()

The finished() helper with { cleanup: true } ensures proper resource cleanup on errors.


858-865: LGTM - Clean separation of validation and insertion.

The insert method now properly separates concerns:

  1. Validates content via _validateFile (which may convert streams to buffers)
  2. Delegates to _doInsert for actual file creation

This ensures validation occurs before any file is written to disk.

@d-gubert d-gubert force-pushed the refactor/s3-large-uploads branch 3 times, most recently from 51ec79d to 396ebd5 Compare January 13, 2026 21:05
@d-gubert d-gubert changed the title refactor: large file upload to S3 causing high resource usage fix: prevent file content buffering in user uploads Jan 13, 2026
@d-gubert d-gubert mentioned this pull request Jan 14, 2026
@ricardogarim ricardogarim marked this pull request as ready for review January 15, 2026 10:58
@ricardogarim ricardogarim requested review from a team as code owners January 15, 2026 10:58
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 16 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/meteor/app/api/server/lib/MultipartUploadHandler.ts">

<violation number="1" location="apps/meteor/app/api/server/lib/MultipartUploadHandler.ts:88">
P2: `parseRequest` never resolves/rejects when the required file is missing, because `writeStreamFinished` stays `false` and the `finish` handler can’t trip `tryResolve`. Initialize `writeStreamFinished` to `true` so the no-file path rejects with `error-no-file` instead of hanging.</violation>
</file>

<file name="apps/meteor/server/ufs/ufs-local.spec.ts">

<violation number="1" location="apps/meteor/server/ufs/ufs-local.spec.ts:103">
P2: `LocalStore.delete` only calls `fs.promises.unlink` once, so this test should keep asserting `calledOnce`. Expecting two calls will cause the test to fail immediately.</violation>
</file>

<file name="apps/meteor/app/file-upload/server/lib/FileUpload.ts">

<violation number="1" location="apps/meteor/app/file-upload/server/lib/FileUpload.ts:416">
P2: Stripping EXIF metadata even when rotation is disabled removes the only orientation data, so images that rely on EXIF rotation now display incorrectly when `Message_Attachments_Strip_Exif` is true but `FileUpload_RotateImages` is false.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@d-gubert d-gubert force-pushed the refactor/s3-large-uploads branch from 0b0841f to 92bada3 Compare January 16, 2026 13:39
Copy link
Contributor

@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: 3

🤖 Fix all issues with AI agents
In `@apps/meteor/app/api/server/lib/MultipartUploadHandler.ts`:
- Around line 129-135: The attached transform error handlers only call
file.destroy(), which can leave the writeStream open and the multipart promise
unresolved; update the error handling for each transform in
MultipartUploadHandler so that on 'error' it also destroys/closes the
writeStream, rejects the active upload promise (or calls the upload failure
callback), and ensures temp file cleanup; reference options.transforms,
transform.on('error', ...), file.destroy, currentStream, and writeStream and
make the handler perform file.destroy(), writeStream.destroy()/end(), and
surface the error to the awaiting promise so cleanup and rejection occur.

In `@apps/meteor/app/livechat/imports/server/rest/upload.ts`:
- Around line 49-53: The file validation branch in the upload handler currently
returns API.v1.failure when fileUploadIsValidContentType(file.mimetype) is false
but never removes the temporary file created by parseRequest; modify the branch
in the handler that checks fileUploadIsValidContentType to synchronously or
asynchronously delete the temp file (using fs.unlink or fs.unlinkSync)
referenced by the parsed file object (e.g., file.path or file.tmpPath returned
from parseRequest) inside a try/catch before returning API.v1.failure so the
temp file is always cleaned up on validation failure.

In `@packages/http-router/src/Router.ts`:
- Around line 158-162: The early-return when contentType includes
'multipart/form-data' is correct but the two inline comments must be removed;
edit the Router.ts block containing the contentType check (the condition
referencing contentType and returning parsedBody) to keep the conditional return
as-is and delete the comment lines that reference UploadService.parse or
file-upload rationale (tests are in Router.spec.ts and uploads are handled by
UploadService.parse), leaving only the if
(contentType?.includes('multipart/form-data')) { return parsedBody; } behavior.
🧹 Nitpick comments (5)
.changeset/twelve-sheep-accept.md (1)

6-6: Consider expanding the description to highlight key benefits.

The description accurately captures the core improvement but could be more informative for changelog readers. Consider mentioning:

  • Prevents crashes during large file uploads
  • Reduces memory consumption and resource usage
  • Streams files directly to disk instead of buffering in memory
📝 Suggested enhancement
-Improves file upload flow to prevent buffering of contents in memory
+Improves file upload flow to prevent buffering of contents in memory, reducing memory consumption and preventing crashes during large file uploads by streaming files directly to disk
packages/http-router/src/Router.ts (1)

159-160: Remove inline comments to comply with no-comments guideline.

Inline comments in implementation files are discouraged here. Consider removing these and rely on the PR description/ADR for rationale.

♻️ Suggested change
-			// Don't parse multipart here, routes handle it manually via UploadService.parse()
-			// since multipart/form-data is only used for file uploads
As per coding guidelines, avoid code comments in the implementation.
apps/meteor/tests/e2e/image-upload.spec.ts (2)

47-52: Encode intent in the describe name instead of inline comment.

The new comment is explanatory but conflicts with the “avoid code comments in implementation” guideline. Consider moving the rationale into the describe title and removing the comment.

♻️ Proposed refactor
-	test.describe('strip exif enabled', () => {
+	test.describe('strip exif enabled (rotation disabled)', () => {
 		test.beforeAll(async ({ api }) => {
 			await setSettingValueById(api, 'Message_Attachments_Strip_Exif', true);
-			// Image rotation now happens before EXIF stripping, so we need to disable it to properly test it
 			await setSettingValueById(api, 'FileUpload_RotateImages', false);
 		});

As per coding guidelines, avoid code comments in implementation.


9-16: Type settings as booleans for accurate test typing.

These settings are boolean configs stored to preserve state; keeping them as unknown weakens type safety. Narrow to boolean with an explicit cast after retrieval, consistent with how retention-policy.spec.ts handles similar boolean settings. This aligns with the Playwright test typing guideline.

♻️ Proposed refactor
-	let defaultStripSetting: unknown;
-	let defaultRotateSetting: unknown;
+	let defaultStripSetting: boolean;
+	let defaultRotateSetting: boolean;
	let poHomeChannel: HomeChannel;
	let targetChannel: string;

	test.beforeAll(async ({ api }) => {
-		defaultStripSetting = await getSettingValueById(api, 'Message_Attachments_Strip_Exif');
-		defaultRotateSetting = await getSettingValueById(api, 'FileUpload_RotateImages');
+		defaultStripSetting = (await getSettingValueById(api, 'Message_Attachments_Strip_Exif')) as boolean;
+		defaultRotateSetting = (await getSettingValueById(api, 'FileUpload_RotateImages')) as boolean;
apps/meteor/app/api/server/lib/MultipartUploadHandler.ts (1)

192-201: Minor: Inconsistent rejection style.

Line 196 uses return Promise.reject(...) while other rejection paths use the reject() callback. Consider using reject() directly for consistency, or return early before piping if no body exists.

Suggested consistency fix
 		if (request instanceof IncomingMessage) {
 			request.pipe(bb);
 		} else {
 			if (!request.body) {
-				return Promise.reject(new MeteorError('error-no-body', 'Request has no body'));
+				reject(new MeteorError('error-no-body', 'Request has no body'));
+				return promise;
 			}
 
 			const nodeStream = Readable.fromWeb(request.body as any);
 			nodeStream.pipe(bb);
 		}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0841f and 92bada3.

📒 Files selected for processing (19)
  • .changeset/twelve-sheep-accept.md
  • apps/meteor/app/api/server/definition.ts
  • apps/meteor/app/api/server/lib/MultipartUploadHandler.ts
  • apps/meteor/app/api/server/middlewares/logger.ts
  • apps/meteor/app/api/server/router.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
  • apps/meteor/app/livechat/imports/server/rest/upload.ts
  • apps/meteor/server/startup/initialData.ts
  • apps/meteor/server/ufs/ufs-filter.ts
  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/server/ufs/ufs-methods.ts
  • apps/meteor/server/ufs/ufs-store.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
  • apps/meteor/tests/end-to-end/api/rooms.ts
  • packages/http-router/src/Router.spec.ts
  • packages/http-router/src/Router.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/meteor/server/startup/initialData.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/ufs/ufs-store.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • packages/http-router/src/Router.spec.ts
  • apps/meteor/server/ufs/ufs-methods.ts
  • apps/meteor/tests/end-to-end/api/rooms.ts
  • apps/meteor/app/api/server/definition.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • packages/http-router/src/Router.ts
  • apps/meteor/server/ufs/ufs-filter.ts
  • apps/meteor/app/api/server/router.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
  • apps/meteor/app/api/server/lib/MultipartUploadHandler.ts
  • apps/meteor/app/livechat/imports/server/rest/upload.ts
  • apps/meteor/app/api/server/middlewares/logger.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
apps/meteor/tests/e2e/**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

apps/meteor/tests/e2e/**/*.spec.ts: All test files must be created in apps/meteor/tests/e2e/ directory
Avoid using page.locator() in Playwright tests - always prefer semantic locators such as page.getByRole(), page.getByLabel(), page.getByText(), or page.getByTitle()
Use test.beforeAll() and test.afterAll() for setup/teardown in Playwright tests
Use test.step() for complex test scenarios to improve organization in Playwright tests
Group related tests in the same file
Utilize Playwright fixtures (test, page, expect) for consistency in test files
Prefer web-first assertions (toBeVisible, toHaveText, etc.) in Playwright tests
Use expect matchers for assertions (toEqual, toContain, toBeTruthy, toHaveLength, etc.) instead of assert statements in Playwright tests
Use page.waitFor() with specific conditions instead of hardcoded timeouts in Playwright tests
Implement proper wait strategies for dynamic content in Playwright tests
Maintain test isolation between test cases in Playwright tests
Ensure clean state for each test execution in Playwright tests
Ensure tests run reliably in parallel without shared state conflicts

Files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
apps/meteor/tests/e2e/**/*.{ts,spec.ts}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

apps/meteor/tests/e2e/**/*.{ts,spec.ts}: Store commonly used locators in variables/constants for reuse
Follow Page Object Model pattern consistently in Playwright tests

Files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 38071
File: apps/meteor/app/apps/server/bridges/listeners.ts:257-271
Timestamp: 2026-01-15T22:03:20.672Z
Learning: In the file upload pipeline (apps/meteor/app/apps/server/bridges/listeners.ts), temporary files are created by the server in the same filesystem, so symlinks between temp files are safe and don't require cross-filesystem fallbacks.
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure clean state for each test execution in Playwright tests

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Maintain test isolation between test cases in Playwright tests

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.step()` for complex test scenarios to improve organization in Playwright tests

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/server/ufs/ufs-local.spec.ts
📚 Learning: 2026-01-15T22:03:20.672Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 38071
File: apps/meteor/app/apps/server/bridges/listeners.ts:257-271
Timestamp: 2026-01-15T22:03:20.672Z
Learning: In the file upload pipeline (apps/meteor/app/apps/server/bridges/listeners.ts), temporary files are created by the server in the same filesystem, so symlinks between temp files are safe and don't require cross-filesystem fallbacks.

Applied to files:

  • .changeset/twelve-sheep-accept.md
  • apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts
  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
  • apps/meteor/app/api/server/lib/MultipartUploadHandler.ts
  • apps/meteor/app/livechat/imports/server/rest/upload.ts
📚 Learning: 2025-09-15T13:10:30.049Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: packages/http-router/src/Router.ts:416-425
Timestamp: 2025-09-15T13:10:30.049Z
Learning: In packages/http-router/src/Router.ts, the dispatch() method's use of replaceAll('//', '/') on the full path is acceptable because URL normalization and query string handling is performed by the caller function before dispatch() is invoked.

Applied to files:

  • packages/http-router/src/Router.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to **/*.{ts,tsx,js} : Avoid code comments in the implementation

Applied to files:

  • packages/http-router/src/Router.ts
📚 Learning: 2025-11-19T18:20:07.720Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: packages/i18n/src/locales/en.i18n.json:918-921
Timestamp: 2025-11-19T18:20:07.720Z
Learning: Repo: RocketChat/Rocket.Chat — i18n/formatting
Learning: This repository uses a custom message formatting parser in UI blocks/messages; do not assume standard Markdown rules. For keys like Call_ended_bold, Call_not_answered_bold, Call_failed_bold, and Call_transferred_bold in packages/i18n/src/locales/en.i18n.json, retain the existing single-asterisk emphasis unless maintainers request otherwise.

Applied to files:

  • packages/http-router/src/Router.ts
📚 Learning: 2025-10-07T14:53:31.040Z
Learnt from: cardoso
Repo: RocketChat/Rocket.Chat PR: 36942
File: apps/meteor/client/lib/e2ee/binary.ts:12-25
Timestamp: 2025-10-07T14:53:31.040Z
Learning: In apps/meteor/client/lib/e2ee/binary.ts, the Binary.toArrayBuffer function uses charCodeAt() for Latin-1 encoding rather than TextEncoder for UTF-8 encoding. The team has decided to keep this approach for E2EE password/key material encoding.

Applied to files:

  • apps/meteor/app/file-upload/server/lib/FileUpload.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.beforeAll()` and `test.afterAll()` for setup/teardown in Playwright tests

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.{ts,spec.ts} : Store commonly used locators in variables/constants for reuse

Applied to files:

  • apps/meteor/tests/e2e/image-upload.spec.ts
🧬 Code graph analysis (2)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)
apps/meteor/app/api/server/lib/MultipartUploadHandler.ts (1)
  • MultipartUploadHandler (31-205)
apps/meteor/app/livechat/imports/server/rest/upload.ts (2)
apps/meteor/app/api/server/lib/MultipartUploadHandler.ts (1)
  • MultipartUploadHandler (31-205)
apps/meteor/app/file-upload/server/lib/FileUpload.ts (1)
  • FileUpload (113-694)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (16)
apps/meteor/server/ufs/ufs-local.spec.ts (1)

102-103: LGTM!

The updated assertion and comment correctly reflect the new temp-file cleanup behavior introduced by the streaming upload refactor. The two unlink calls (temp file + actual file via removeById()) are expected behavior as confirmed in prior discussion.

.changeset/twelve-sheep-accept.md (1)

1-4: Verify that "patch" is the appropriate semver bump level.

The PR introduces a new MultipartUploadHandler class, updates _doInsert to accept file paths, and refactors the upload pipeline significantly. Depending on whether these changes affect public APIs or only internal implementation:

  • If the public API remains compatible and this is an internal refactor, patch is appropriate.
  • If new public classes/methods are exposed, consider minor.
  • If existing public signatures changed in breaking ways, consider major.

Please confirm that the affected APIs are internal or that backwards compatibility is maintained for public consumers of these packages.

apps/meteor/app/api/server/middlewares/logger.ts (1)

13-21: LGTM!

The conditional logic correctly avoids consuming the request body stream for multipart requests, which is essential for the new streaming upload pipeline. Using includes() properly handles content-type headers with boundary parameters.

apps/meteor/app/api/server/router.ts (1)

9-15: LGTM!

The HonoContext type alias and the incoming field in APIActionContext are correctly aligned, enabling API handlers to access the raw IncomingMessage for stream-based multipart parsing.

Also applies to: 26-27

apps/meteor/server/ufs/ufs-filter.ts (1)

10-10: LGTM!

The type widening from ReadableStream | Buffer to Buffer | string correctly supports the new file-path-based content handling, maintaining backward compatibility for existing Buffer usage while enabling string paths for the streaming upload pipeline.

Also applies to: 62-62, 140-140

apps/meteor/app/api/server/lib/MultipartUploadHandler.ts (3)

180-182: LGTM - filesLimit error handling fixed.

The filesLimit event now correctly rejects with a MeteorError for consistent error handling, addressing the previous review concern.


85-102: LGTM - Promise resolution logic is sound.

The filePendingCount approach correctly handles both the "no file uploaded" case (rejecting with error-no-file unless fileOptional) and ensures the promise doesn't hang when Busboy finishes before the write completes.


46-62: LGTM - stripExifFromFile implementation.

The pipeline-based EXIF stripping with atomic rename and proper cleanup on error is well implemented.

apps/meteor/app/livechat/imports/server/rest/upload.ts (1)

65-68: The null check is unnecessary—fileStore.insert() throws on error rather than returning falsy.

The insert method always returns a Promise that either resolves with an IUpload object or throws an exception (via ufsComplete). Temporary files are automatically cleaned up by ufsComplete on success (line 60) and on any failure (lines 51, 74). Remove the if (!uploadedFile) check and use try-catch if error handling is needed.

apps/meteor/app/file-upload/server/lib/FileUpload.spec.ts (1)

34-34: Good test harness alignment with multipart handler.

Stubbing the new dependency keeps the proxyquire setup consistent with the updated upload pipeline.

apps/meteor/app/file-upload/server/lib/FileUpload.ts (6)

8-10: Import updates fit the new streaming flow.

These additions align with the new temp-file and stream-based handling.

Also applies to: 34-34


139-194: Verify app event payload compatibility with path-based content.

Line 139 and Line 194 now allow content to be a string path; please confirm app bridge typings and any IPreFileUpload consumers handle string paths (not only Buffer).


391-419: EXIF handling flow looks solid.

The cached settings and size updates integrate cleanly with the new MultipartUploadHandler path.


805-830: Confirm stream buffering won’t reintroduce large memory spikes.

Line 815–819 buffers the entire stream; please verify only bounded-size streams reach this path (or enforce a size cap) so large uploads don’t regress memory usage.


834-852: Check Node runtime support for finished(..., { cleanup: true }).

If your supported Node versions don’t include the cleanup option, consider pipeline (or manual teardown) to avoid runtime errors.


859-863: Validation-before-insert flow is clean.

Nice consolidation with _validateFile before _doInsert.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Member

@yash-rajpal yash-rajpal left a comment

Choose a reason for hiding this comment

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

In my testing, the images having wrong orientation are not being re-oriented as expected, even when disabling Message_Attachments_Strip_Exif setting.

This works on develop when Message_Attachments_Strip_Exif setting is disabled. It should work regardless of the setting value.

Copy link
Member

@yash-rajpal yash-rajpal left a comment

Choose a reason for hiding this comment

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

After internal discussion, we decided to fix the image orientation behavior with different PR.

@kodiakhq kodiakhq bot merged commit ccd7694 into develop Jan 16, 2026
44 checks passed
@kodiakhq kodiakhq bot deleted the refactor/s3-large-uploads branch January 16, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants