feat: migrate batch4 client DDP callers + add 6 REST endpoints#40728
feat: migrate batch4 client DDP callers + add 6 REST endpoints#40728ggazzo wants to merge 5 commits into
Conversation
New endpoints (with deprecation logs on their DDP counterparts):
- POST /v1/users.verifyEmail (verifyEmail + afterVerifyEmail)
- POST /v1/cloud.connectWorkspace (cloud:connectWorkspace)
- POST /v1/permissions.addRole (authorization:addPermissionToRole)
- POST /v1/permissions.removeRole (authorization:removeRoleFromPermission)
- POST /v1/integrations.history.clear (clearIntegrationHistory)
- POST /v1/integrations.outgoing.replay (replayOutgoingIntegration)
- GET /v1/rooms.getByTypeAndName (getRoomByTypeAndName)
Existing endpoint extensions:
- POST /v1/users.setAvatar now accepts an optional 'service' multipart
field, storing it as the user's avatarOrigin (replaces the
setAvatarFromService DDP method).
Server-side hook:
- afterLogoutCleanUpCallback + Apps.IPostUserLoggedOut fire from a
new Accounts.onLogout handler and from POST /v1/users.logout, so the
client no longer needs to invoke logoutCleanUp after a DDP logout.
Client callers swapped:
- UserProvider drops sdk.call('logoutCleanUp')
- useUpdateAvatar sends 'service' through /v1/users.setAvatar
- useOpenRoom + EmbeddedPreload → /v1/rooms.getByTypeAndName
- RegisterWorkspaceTokenModal → /v1/cloud.connectWorkspace
- meteor/startup/accounts.ts collapses verify+after into single REST
- PermissionsTable → /v1/permissions.{add,remove}Role
- OutgoingWebhookHistoryPage + HistoryItem → integrations endpoints
DDP methods stay registered with deprecation logs pointing at the new
routes for external SDK/mobile clients until 9.0.0 removes them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Looks like this PR is not ready to merge, because of the following issues:
Please fix the issues and try again If you have any trouble, please check the PR guidelines |
🦋 Changeset detectedLatest commit: 3eacd95 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
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 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent review details⏰ 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). (4)
WalkthroughSeven DDP methods are migrated to REST endpoints: logout cleanup, avatar service, cloud workspace, email verification, permission-role add/remove, and integration history clear/replay. Shared server helpers are extracted, deprecated DDP methods delegate to those helpers with deprecation logs, new REST endpoints are registered with typed AJV validation, and client-side hooks switch from ChangesDDP-to-REST Endpoint Migrations
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #40728 +/- ##
===========================================
- Coverage 70.14% 70.11% -0.03%
===========================================
Files 3355 3357 +2
Lines 129261 129284 +23
Branches 22340 22771 +431
===========================================
- Hits 90673 90653 -20
- Misses 35293 35340 +47
+ Partials 3295 3291 -4
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx (1)
55-63:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix connect-workspace success handling: the REST endpoint
cloud.connectWorkspacealways returnsAPI.v1.success()afterawait connectWorkspace(this.bodyParams.token)and its 200 response schema requires{ success: true }, butconnectWorkspace(token)can returnfalseon connection failures (it does not necessarily throw for invalid/used tokens). This would cause the modal to show a misleading “Connected” toast whenconnectWorkspace({ token })resolves falsy instead of rejecting—either makeconnectWorkspacethrow on failure or restore an explicit success/connected check.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx` around lines 55 - 63, connectWorkspace may resolve to a falsy value on failure instead of throwing, so update the success handling in RegisterWorkspaceTokenModal to check the resolved value from connectWorkspace({ token }) before considering it connected: call const result = await connectWorkspace({ token }); if (result) then call setModal(null) and dispatchToastMessage({ type: 'success', message: t('Connected') }); otherwise treat it as a failure by dispatching an error toast (with a helpful message) and calling setError(true); keep the existing catch block to handle thrown errors from connectWorkspace as well.apps/meteor/client/providers/UserProvider/UserProvider.tsx (1)
158-160:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftFix broken logout handler wiring in UserProvider (
eeis undefined).
apps/meteor/client/providers/UserProvider/UserProvider.tsx(lines 158-160) definesonLogout: (cb) => ee.on('logout', cb), butee(and anyEmitterwiring) is not declared/imported in this module—so registering anonLogouthandler will throw and subscribers won’t be notified.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/client/providers/UserProvider/UserProvider.tsx` around lines 158 - 160, The onLogout handler in UserProvider is wired to an undefined emitter variable (ee) causing runtime errors; replace this with the module's actual event mechanism: import or reference the existing EventEmitter instance used elsewhere (or create a local emitter) and wire onLogout to that emitter's on method; specifically, fix the onLogout: (cb) => ... entry in UserProvider to use the proper emitter (the same emitter used to emit 'logout' events in this module or application) so subscribers are registered and will be notified (ensure the emitter is declared/imported and exported consistently with other functions that call emitter.emit('logout')).
🧹 Nitpick comments (2)
apps/meteor/server/hooks/userLogoutCleanUp.ts (1)
8-10: ⚡ Quick winHandle rejection of the deferred cleanup promise.
void afterLogoutCleanUpCallback.run(user)is fire-and-forget insidesetImmediate, so a rejection escapes as an unhandled promise rejection (noawait, no.catch). Attach a catch to log/swallow it.♻️ Proposed fix
setImmediate(() => { - void afterLogoutCleanUpCallback.run(user); + afterLogoutCleanUpCallback.run(user).catch((err) => { + SystemLogger.error({ msg: 'Error running afterLogoutCleanUpCallback', err }); + }); });Adjust the logger import to match this module's conventions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/server/hooks/userLogoutCleanUp.ts` around lines 8 - 10, The deferred cleanup launched in setImmediate currently calls void afterLogoutCleanUpCallback.run(user) which can reject and become an unhandled promise; change that to explicitly handle rejection by attaching a .catch on the returned promise (or await inside an async wrapper) and log or swallow the error using this module's logger (use the same logger import convention as this file) so any rejection from afterLogoutCleanUpCallback.run(user) is caught and reported.apps/meteor/client/meteor/startup/accounts.ts (1)
9-16: Remove unusedverifyEmailDDP module augmentation.
apps/meteor/client/meteor/startup/accounts.tsnow verifies viasdk.rest.post('/v1/users.verifyEmail', { token }), and thedeclare module '@rocket.chat/ddp-client' { interface ServerMethods { verifyEmail(...) } }block (lines 9-16, including its explanatory comment) appears unused. Consider removing it to avoid stale typing.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/client/meteor/startup/accounts.ts` around lines 9 - 16, The declared augmentation of the DDP types is stale: remove the entire `declare module '`@rocket.chat/ddp-client`' { interface ServerMethods { verifyEmail(token: string): void; } }` block (and its explanatory comment) from the file so the unused `verifyEmail` DDP augmentation is no longer present; after removal, run the TypeScript build or linter to confirm no remaining references to `ServerMethods.verifyEmail` and update any imports/usages that should use `sdk.rest.post('/v1/users.verifyEmail', { token })` instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/meteor/app/api/server/v1/users.ts`:
- Around line 2101-2131: The user lookup using Users.findOne({
'services.email.verificationTokens.token': token }) is performed before calling
Meteor.callAsync('verifyEmail', token), which can cause
runAfterVerifyEmail(user._id) to be skipped if the pre-verify query returned
null; move the Users.findOne call to after the Meteor.callAsync('verifyEmail',
token) (or re-fetch the user after verifyEmail returns) so you always look up
the user post-verification and then call runAfterVerifyEmail with the found
user's _id; update the users.verifyEmail handler accordingly to ensure the
lookup and runAfterVerifyEmail happen only after Meteor.callAsync('verifyEmail',
token) succeeds.
In `@apps/meteor/client/hooks/useUpdateAvatar.ts`:
- Around line 56-61: The avatar upload is appending a data-URI string as a file
which fails multipart handling in saveAvatarAction/users.setAvatar; in
useUpdateAvatar detect when avatarObj.blob is a string (UserAvatarSuggestion
data:<type>;base64,...) and convert that data URI into a real Blob (decode
base64, create Uint8Array/Blob with the correct MIME type and optional filename)
before calling formData.append('image', blob); alternatively tighten the
isServiceObject guard to require avatarObj.blob instanceof Blob and handle
string blobs by converting them to Blob or routing to the suggestion upload
path; ensure saveAvatarAction/getUploadFormData still receives a real File/Blob
for the 'image' field.
---
Outside diff comments:
In `@apps/meteor/client/providers/UserProvider/UserProvider.tsx`:
- Around line 158-160: The onLogout handler in UserProvider is wired to an
undefined emitter variable (ee) causing runtime errors; replace this with the
module's actual event mechanism: import or reference the existing EventEmitter
instance used elsewhere (or create a local emitter) and wire onLogout to that
emitter's on method; specifically, fix the onLogout: (cb) => ... entry in
UserProvider to use the proper emitter (the same emitter used to emit 'logout'
events in this module or application) so subscribers are registered and will be
notified (ensure the emitter is declared/imported and exported consistently with
other functions that call emitter.emit('logout')).
In
`@apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx`:
- Around line 55-63: connectWorkspace may resolve to a falsy value on failure
instead of throwing, so update the success handling in
RegisterWorkspaceTokenModal to check the resolved value from connectWorkspace({
token }) before considering it connected: call const result = await
connectWorkspace({ token }); if (result) then call setModal(null) and
dispatchToastMessage({ type: 'success', message: t('Connected') }); otherwise
treat it as a failure by dispatching an error toast (with a helpful message) and
calling setError(true); keep the existing catch block to handle thrown errors
from connectWorkspace as well.
---
Nitpick comments:
In `@apps/meteor/client/meteor/startup/accounts.ts`:
- Around line 9-16: The declared augmentation of the DDP types is stale: remove
the entire `declare module '`@rocket.chat/ddp-client`' { interface ServerMethods {
verifyEmail(token: string): void; } }` block (and its explanatory comment) from
the file so the unused `verifyEmail` DDP augmentation is no longer present;
after removal, run the TypeScript build or linter to confirm no remaining
references to `ServerMethods.verifyEmail` and update any imports/usages that
should use `sdk.rest.post('/v1/users.verifyEmail', { token })` instead.
In `@apps/meteor/server/hooks/userLogoutCleanUp.ts`:
- Around line 8-10: The deferred cleanup launched in setImmediate currently
calls void afterLogoutCleanUpCallback.run(user) which can reject and become an
unhandled promise; change that to explicitly handle rejection by attaching a
.catch on the returned promise (or await inside an async wrapper) and log or
swallow the error using this module's logger (use the same logger import
convention as this file) so any rejection from
afterLogoutCleanUpCallback.run(user) is caught and reported.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d1570026-d78a-4d6d-a109-e628c9a159b5
📒 Files selected for processing (42)
.changeset/ddp-migrate-batch4-callers.md.changeset/rest-batch4-logout-cleanup.md.changeset/rest-cloud-connect-workspace.md.changeset/rest-integrations-history-clear-replay.md.changeset/rest-permissions-add-remove-role.md.changeset/rest-rooms-get-by-type-and-name.md.changeset/rest-users-setavatar-service.md.changeset/rest-users-verify-email.mdapps/meteor/app/api/server/v1/cloud.tsapps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/app/api/server/v1/users.tsapps/meteor/app/authorization/server/functions/permissionRole.tsapps/meteor/app/authorization/server/methods/addPermissionToRole.tsapps/meteor/app/authorization/server/methods/removeRoleFromPermission.tsapps/meteor/app/cloud/server/methods.tsapps/meteor/app/integrations/server/functions/clearIntegrationHistory.tsapps/meteor/app/integrations/server/methods/clearIntegrationHistory.tsapps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.tsapps/meteor/client/hooks/useUpdateAvatar.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/client/providers/UserProvider/UserProvider.tsxapps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsxapps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsxapps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsxapps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsxapps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsxapps/meteor/server/hooks/index.tsapps/meteor/server/hooks/userLogoutCleanUp.tsapps/meteor/server/lib/users/runAfterVerifyEmail.tsapps/meteor/server/methods/afterVerifyEmail.tsapps/meteor/server/methods/logoutCleanUp.tsapps/meteor/server/publications/room/index.tspackages/rest-typings/src/v1/cloud.tspackages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.tspackages/rest-typings/src/v1/integrations/IntegrationsReplayProps.tspackages/rest-typings/src/v1/integrations/index.tspackages/rest-typings/src/v1/integrations/integrations.tspackages/rest-typings/src/v1/rooms.tspackages/rest-typings/src/v1/users.ts
📜 Review details
⏰ 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). (5)
- GitHub Check: 📦 Build Packages
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Hacktron Security Check
- GitHub Check: CodeQL-Build
- GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{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:
packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.tspackages/rest-typings/src/v1/integrations/index.tsapps/meteor/server/hooks/userLogoutCleanUp.tsapps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsxpackages/rest-typings/src/v1/cloud.tsapps/meteor/server/hooks/index.tsapps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/app/cloud/server/methods.tspackages/rest-typings/src/v1/users.tspackages/rest-typings/src/v1/integrations/integrations.tsapps/meteor/server/lib/users/runAfterVerifyEmail.tsapps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsxapps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsxapps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsxpackages/rest-typings/src/v1/rooms.tsapps/meteor/app/authorization/server/functions/permissionRole.tsapps/meteor/app/integrations/server/methods/clearIntegrationHistory.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/cloud.tsapps/meteor/server/methods/afterVerifyEmail.tsapps/meteor/server/methods/logoutCleanUp.tsapps/meteor/app/integrations/server/functions/clearIntegrationHistory.tspackages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.tsapps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsxapps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.tsapps/meteor/server/publications/room/index.tsapps/meteor/app/authorization/server/methods/addPermissionToRole.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/client/hooks/useUpdateAvatar.tsapps/meteor/app/api/server/v1/users.tsapps/meteor/app/authorization/server/methods/removeRoleFromPermission.tsapps/meteor/client/providers/UserProvider/UserProvider.tsx
🧠 Learnings (14)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.
Applied to files:
packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.tspackages/rest-typings/src/v1/integrations/index.tsapps/meteor/server/hooks/userLogoutCleanUp.tspackages/rest-typings/src/v1/cloud.tsapps/meteor/server/hooks/index.tsapps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/app/cloud/server/methods.tspackages/rest-typings/src/v1/users.tspackages/rest-typings/src/v1/integrations/integrations.tsapps/meteor/server/lib/users/runAfterVerifyEmail.tspackages/rest-typings/src/v1/rooms.tsapps/meteor/app/authorization/server/functions/permissionRole.tsapps/meteor/app/integrations/server/methods/clearIntegrationHistory.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/cloud.tsapps/meteor/server/methods/afterVerifyEmail.tsapps/meteor/server/methods/logoutCleanUp.tsapps/meteor/app/integrations/server/functions/clearIntegrationHistory.tspackages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.tsapps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.tsapps/meteor/server/publications/room/index.tsapps/meteor/app/authorization/server/methods/addPermissionToRole.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/client/hooks/useUpdateAvatar.tsapps/meteor/app/api/server/v1/users.tsapps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.
Applied to files:
packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.tspackages/rest-typings/src/v1/integrations/index.tsapps/meteor/server/hooks/userLogoutCleanUp.tspackages/rest-typings/src/v1/cloud.tsapps/meteor/server/hooks/index.tsapps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/app/cloud/server/methods.tspackages/rest-typings/src/v1/users.tspackages/rest-typings/src/v1/integrations/integrations.tsapps/meteor/server/lib/users/runAfterVerifyEmail.tspackages/rest-typings/src/v1/rooms.tsapps/meteor/app/authorization/server/functions/permissionRole.tsapps/meteor/app/integrations/server/methods/clearIntegrationHistory.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/cloud.tsapps/meteor/server/methods/afterVerifyEmail.tsapps/meteor/server/methods/logoutCleanUp.tsapps/meteor/app/integrations/server/functions/clearIntegrationHistory.tspackages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.tsapps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.tsapps/meteor/server/publications/room/index.tsapps/meteor/app/authorization/server/methods/addPermissionToRole.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/client/hooks/useUpdateAvatar.tsapps/meteor/app/api/server/v1/users.tsapps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.
Applied to files:
packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.tspackages/rest-typings/src/v1/integrations/index.tsapps/meteor/server/hooks/userLogoutCleanUp.tsapps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsxpackages/rest-typings/src/v1/cloud.tsapps/meteor/server/hooks/index.tsapps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/app/cloud/server/methods.tspackages/rest-typings/src/v1/users.tspackages/rest-typings/src/v1/integrations/integrations.tsapps/meteor/server/lib/users/runAfterVerifyEmail.tsapps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsxapps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsxapps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsxpackages/rest-typings/src/v1/rooms.tsapps/meteor/app/authorization/server/functions/permissionRole.tsapps/meteor/app/integrations/server/methods/clearIntegrationHistory.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/cloud.tsapps/meteor/server/methods/afterVerifyEmail.tsapps/meteor/server/methods/logoutCleanUp.tsapps/meteor/app/integrations/server/functions/clearIntegrationHistory.tspackages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.tsapps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsxapps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.tsapps/meteor/server/publications/room/index.tsapps/meteor/app/authorization/server/methods/addPermissionToRole.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/client/hooks/useUpdateAvatar.tsapps/meteor/app/api/server/v1/users.tsapps/meteor/app/authorization/server/methods/removeRoleFromPermission.tsapps/meteor/client/providers/UserProvider/UserProvider.tsx
📚 Learning: 2026-05-11T23:14:59.316Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 40469
File: packages/rest-typings/src/v1/users.ts:337-337
Timestamp: 2026-05-11T23:14:59.316Z
Learning: In Rocket.Chat REST endpoint typings (e.g., packages/rest-typings/src/v1/users.ts and other rest-typings files), keep the established convention of deriving field types from the domain model (e.g., use IUser indexed access like IUser['statusExpiresAt']) rather than swapping individual fields to serialized primitives (like string) in an ad-hoc way. If a truly different “serialized” representation is needed, perform the refactor consistently across the codebase (not just a single endpoint/field) and ensure all related REST typings stay aligned with the shared serialization types.
Applied to files:
packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.tspackages/rest-typings/src/v1/integrations/index.tspackages/rest-typings/src/v1/cloud.tspackages/rest-typings/src/v1/users.tspackages/rest-typings/src/v1/integrations/integrations.tspackages/rest-typings/src/v1/rooms.tspackages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
📚 Learning: 2026-03-16T21:50:37.589Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: .changeset/migrate-users-register-openapi.md:3-3
Timestamp: 2026-03-16T21:50:37.589Z
Learning: For changes related to OpenAPI migrations in Rocket.Chat/OpenAPI, when removing endpoint types and validators from rocket.chat/rest-typings (e.g., UserRegisterParamsPOST, /v1/users.register) document this as a minor changeset (not breaking) per RocketChat/Rocket.Chat-Open-API#150 Rule 7. Note that the endpoint type is re-exposed via a module augmentation .d.ts in the consuming package (e.g., packages/web-ui-registration/src/users-register.d.ts). In reviews, ensure the changeset clearly states: this is a non-breaking change, the major version should not be bumped, and the changeset reflects a minor version bump. Do not treat this as a breaking change during OpenAPI migrations.
Applied to files:
.changeset/rest-users-setavatar-service.md.changeset/rest-permissions-add-remove-role.md.changeset/rest-rooms-get-by-type-and-name.md.changeset/rest-users-verify-email.md.changeset/ddp-migrate-batch4-callers.md.changeset/rest-cloud-connect-workspace.md.changeset/rest-integrations-history-clear-replay.md.changeset/rest-batch4-logout-cleanup.md
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.
Applied to files:
apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsxapps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsxapps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsxapps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsxapps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsxapps/meteor/client/providers/UserProvider/UserProvider.tsx
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.
Applied to files:
apps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/client/hooks/useUpdateAvatar.ts
📚 Learning: 2026-05-11T20:30:35.265Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 40480
File: apps/meteor/client/meteor/startup/accounts.ts:59-61
Timestamp: 2026-05-11T20:30:35.265Z
Learning: In Rocket.Chat’s Meteor client code, when calling `dispatchToastMessage` with `{ type: 'error' }`, pass the raw caught error object as `message` without manual normalization. `dispatchToastMessage` is designed to accept `message: unknown` for error toasts, so avoid converting errors to strings (e.g., `String(error)`) or extracting `error.message` before passing them.
Applied to files:
apps/meteor/client/views/room/hooks/useOpenRoom.tsapps/meteor/client/meteor/startup/accounts.tsapps/meteor/client/hooks/useUpdateAvatar.ts
📚 Learning: 2026-02-23T17:53:06.802Z
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 35995
File: apps/meteor/app/api/server/v1/rooms.ts:1107-1112
Timestamp: 2026-02-23T17:53:06.802Z
Learning: During PR reviews that touch endpoint files under apps/meteor/app/api/server/v1, enforce strict scope: if a PR targets a specific endpoint (e.g., rooms.favorite), do not propose changes to unrelated endpoints (e.g., rooms.invite) unless maintainers explicitly request them. Focus feedback on the touched endpoint's behavior, API surface, and related tests; avoid broad cross-endpoint changes in the same PR unless requested.
Applied to files:
apps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/cloud.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/app/api/server/v1/users.ts
📚 Learning: 2026-02-24T19:09:01.522Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38974
File: apps/meteor/app/api/server/v1/im.ts:220-221
Timestamp: 2026-02-24T19:09:01.522Z
Learning: In Rocket.Chat OpenAPI migration PRs for endpoints under apps/meteor/app/api/server/v1, avoid introducing logic changes. Only perform scope-tight changes that preserve behavior; style-only cleanups (e.g., removing inline comments) may be deferred to follow-ups to keep the migration PR focused.
Applied to files:
apps/meteor/app/api/server/v1/integrations.tsapps/meteor/app/api/server/v1/permissions.tsapps/meteor/app/api/server/v1/cloud.tsapps/meteor/app/api/server/v1/rooms.tsapps/meteor/app/api/server/v1/users.ts
📚 Learning: 2026-03-09T18:39:14.020Z
Learnt from: Harxhit
Repo: RocketChat/Rocket.Chat PR: 39476
File: apps/meteor/server/methods/addAllUserToRoom.ts:0-0
Timestamp: 2026-03-09T18:39:14.020Z
Learning: When implementing batch processing in server methods, favor a single data pass to collect both the items and any derived fields needed for validation. Use the same dataset for both validation and processing to avoid races between validation and execution, and document the approach in code comments. Apply this pattern to similar Meteor Rocket.Chat server methods under apps/meteor/server/methods to prevent race conditions and ensure consistent batch behavior.
Applied to files:
apps/meteor/server/methods/afterVerifyEmail.tsapps/meteor/server/methods/logoutCleanUp.ts
📚 Learning: 2026-04-14T21:10:31.855Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 36292
File: apps/meteor/client/hooks/useHasValidLocationHash.ts:7-12
Timestamp: 2026-04-14T21:10:31.855Z
Learning: When reviewing files in apps/meteor/client/hooks/, do not treat JSDoc-style comments on React hooks (especially exported hooks) as a violation of any “avoid code comments in implementation” guideline. It’s acceptable to use JSDoc to document the public API of exported hooks (e.g., parameter/return types, intended usage), as long as it documents behavior/contracts rather than adding narrative implementation comments.
Applied to files:
apps/meteor/client/hooks/useUpdateAvatar.ts
📚 Learning: 2026-03-15T14:31:25.380Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39647
File: apps/meteor/app/api/server/v1/users.ts:710-757
Timestamp: 2026-03-15T14:31:25.380Z
Learning: Do not flag this type/schema misalignment in the OpenAPI/migration review for apps/meteor/app/api/server/v1/users.ts. The UserCreateParamsPOST type intentionally uses non-optional fields: fields: string and settings?: IUserSettings without an AJV schema entry, carried over from the original rest-typings (PR `#39647`). Treat this as a known pre-existing divergence and document it as a separate follow-up fix; do not block or mark it as a review issue during the migration.
Applied to files:
apps/meteor/app/api/server/v1/users.ts
📚 Learning: 2026-03-16T23:33:11.443Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: apps/meteor/app/api/server/v1/users.ts:862-869
Timestamp: 2026-03-16T23:33:11.443Z
Learning: In rockets OpenAPI/AJV migration reviews for RocketChat/Rocket.Chat, when reviewing migrations that involve apps/meteor/app/api/server/v1/users.ts, do not require or flag a missing query AJV schema for the fields consumed by parseJsonQuery() (i.e., fields, sort, query) as part of this endpoint's migration PR. The addition of global query-param schemas for parseJsonQuery() usage is a cross-cutting concern and out of scope for individual endpoint migrations. Only flag violations related to the specific scope of the migration, not the absence of a query schema for parseJsonQuery() in this file.
Applied to files:
apps/meteor/app/api/server/v1/users.ts
🔇 Additional comments (41)
packages/rest-typings/src/v1/cloud.ts (1)
62-108: LGTM!packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts (1)
1-16: LGTM!packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts (1)
1-18: LGTM!packages/rest-typings/src/v1/integrations/index.ts (1)
3-7: LGTM!packages/rest-typings/src/v1/integrations/integrations.ts (1)
3-9: LGTM!Also applies to: 45-52
packages/rest-typings/src/v1/rooms.ts (1)
131-143: LGTM!Also applies to: 900-904
packages/rest-typings/src/v1/users.ts (1)
375-377: LGTM!apps/meteor/server/lib/users/runAfterVerifyEmail.ts (1)
1-31: LGTM!apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts (1)
1-70: LGTM!apps/meteor/app/authorization/server/functions/permissionRole.ts (1)
1-78: LGTM!apps/meteor/server/publications/room/index.ts (1)
49-104: LGTM!apps/meteor/app/api/server/v1/cloud.ts (1)
292-309: LGTM!apps/meteor/app/api/server/v1/integrations.ts (1)
372-415: LGTM!apps/meteor/app/api/server/v1/permissions.ts (1)
66-83: LGTM!Also applies to: 208-244
apps/meteor/app/api/server/v1/rooms.ts (1)
559-586: LGTM!apps/meteor/app/api/server/v1/users.ts (3)
348-350: LGTM!
1887-1900: LGTM!
41-45: LGTM!apps/meteor/app/cloud/server/methods.ts (1)
115-115: LGTM!apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts (1)
1-20: LGTM!apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts (1)
1-20: LGTM!apps/meteor/app/authorization/server/methods/addPermissionToRole.ts (1)
1-19: LGTM!apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts (1)
1-19: LGTM!apps/meteor/server/methods/afterVerifyEmail.ts (1)
1-28: LGTM!apps/meteor/server/methods/logoutCleanUp.ts (1)
1-23: LGTM!apps/meteor/client/meteor/startup/accounts.ts (1)
49-59: LGTM!apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx (1)
4-4: LGTM!Also applies to: 16-16, 39-42
apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx (1)
3-3: LGTM!Also applies to: 21-21, 53-62
apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx (1)
6-7: LGTM!Also applies to: 26-41
apps/meteor/client/views/room/hooks/useOpenRoom.ts (2)
3-3: LGTM!Also applies to: 22-22
79-79: ⚡ Quick winEnsure REST room payload date fields are deserialized before
Rooms.state.store
apps/meteor/client/views/room/hooks/useOpenRoom.tsforce-casts the REST/v1/rooms.getByTypeAndNameresult (roomData = (await getRoomByTypeAndName(...)).room as IRoom;) and then stores it directly (Rooms.state.store(roomData)). The cached store implementation stores new records without deserializing date fields (it only uses_updatedAt/hasUpdatedAtfor change tracking), so if the REST response provides ISO strings forts,lm,lastMessage.ts/lastMessage._updatedAt/_updatedAt, those strings will be persisted whereIRoomexpectsDate. Convert/normalize these fields before storing (or make the REST client revive them toDate).apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx (1)
1-1: LGTM!Also applies to: 37-37, 49-49
apps/meteor/server/hooks/index.ts (1)
2-2: LGTM!.changeset/ddp-migrate-batch4-callers.md (1)
1-13: LGTM!.changeset/rest-batch4-logout-cleanup.md (1)
1-6: LGTM!.changeset/rest-cloud-connect-workspace.md (1)
1-7: LGTM!.changeset/rest-integrations-history-clear-replay.md (1)
1-7: LGTM!.changeset/rest-permissions-add-remove-role.md (1)
1-7: LGTM!.changeset/rest-rooms-get-by-type-and-name.md (1)
1-7: LGTM!.changeset/rest-users-setavatar-service.md (1)
1-5: LGTM!.changeset/rest-users-verify-email.md (1)
1-6: LGTM!
| API.v1.post( | ||
| 'users.verifyEmail', | ||
| { | ||
| authRequired: false, | ||
| body: ajv.compile<{ token: string }>({ | ||
| type: 'object', | ||
| properties: { | ||
| token: { type: 'string', minLength: 1 }, | ||
| }, | ||
| required: ['token'], | ||
| additionalProperties: false, | ||
| }), | ||
| response: { | ||
| 200: voidSuccessResponse, | ||
| 400: validateBadRequestErrorResponse, | ||
| }, | ||
| }, | ||
| async function action() { | ||
| const { token } = this.bodyParams; | ||
|
|
||
| const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } }); | ||
|
|
||
| await Meteor.callAsync('verifyEmail', token); | ||
|
|
||
| if (user) { | ||
| await runAfterVerifyEmail(user._id); | ||
| } | ||
|
|
||
| return API.v1.success(); | ||
| }, | ||
| ); |
There was a problem hiding this comment.
User lookup should occur after verifyEmail succeeds to avoid skipping cleanup.
The user is looked up before Meteor.callAsync('verifyEmail', token), but the token might be valid even if no user was found due to a race or projection mismatch. If verifyEmail succeeds (marks email verified) but the prior lookup returned null, runAfterVerifyEmail is skipped—leaving the anonymous→user role swap incomplete.
Consider moving the user lookup after the verifyEmail call or re-fetching the user after verification succeeds.
Proposed fix
async function action() {
const { token } = this.bodyParams;
- const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });
-
await Meteor.callAsync('verifyEmail', token);
+ const user = await Users.findOne<Pick<IUser, '_id'>>({ 'emails.verified': true, 'services.email.verificationTokens.token': { $exists: false } }, { projection: { _id: 1 } });
+ // Alternative: lookup by the token that was just consumed (if Meteor stores it differently post-verification)
+ // Or pass userId from verifyEmail if it returns it
+
if (user) {
await runAfterVerifyEmail(user._id);
}
return API.v1.success();
}Note: The exact query after verification depends on how verifyEmail modifies the user document. Verify that the lookup correctly identifies the user whose email was just verified.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/meteor/app/api/server/v1/users.ts` around lines 2101 - 2131, The user
lookup using Users.findOne({ 'services.email.verificationTokens.token': token })
is performed before calling Meteor.callAsync('verifyEmail', token), which can
cause runAfterVerifyEmail(user._id) to be skipped if the pre-verify query
returned null; move the Users.findOne call to after the
Meteor.callAsync('verifyEmail', token) (or re-fetch the user after verifyEmail
returns) so you always look up the user post-verification and then call
runAfterVerifyEmail with the found user's _id; update the users.verifyEmail
handler accordingly to ensure the lookup and runAfterVerifyEmail happen only
after Meteor.callAsync('verifyEmail', token) succeeds.
| const { blob, service } = avatarObj; | ||
| const formData = new FormData(); | ||
| formData.append('userId', userId); | ||
| formData.append('service', service); | ||
| formData.append('image', blob); | ||
| await saveAvatarAction(formData); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 1) AvatarServiceObject definition
ast-grep --pattern 'type AvatarServiceObject = $_'
rg -nP '\bAvatarServiceObject\b' --type=ts -C2
# 2) Where service avatar objects (blob/service) are produced
rg -nP "service\s*:|blob\s*:" --type=ts -g '*Avatar*' -C3
# 3) How /v1/users.setAvatar consumes the multipart 'image' / 'service' fields
rg -nP "users\.setAvatar" --type=ts -A30 -g '**/api/server/v1/users.ts'Repository: RocketChat/Rocket.Chat
Length of output: 15243
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect useUpdateAvatar.ts around the type guards + FormData construction.
sed -n '1,140p' apps/meteor/client/hooks/useUpdateAvatar.ts
# Locate AvatarUrlObj definition and confirm its shape.
rg -n "export type AvatarUrlObj" -S packages/core-typings/src/IUser.ts
sed -n '240,340p' packages/core-typings/src/IUser.ts
# Inspect further down in users.setAvatar handler to see how `image` is used.
rg -n "const image = await getUploadFormData|setUserAvatar\\(" apps/meteor/app/api/server/v1/users.ts
sed -n '300,420p' apps/meteor/app/api/server/v1/users.ts
# Find getUploadFormData implementation to confirm field parsing expectations (best-effort).
rg -n "function getUploadFormData|getUploadFormData\\(" -S apps/meteor/app -g'*.ts' | head -n 50Repository: RocketChat/Rocket.Chat
Length of output: 10318
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect getUploadFormData to see how mimetype/fileBuffer are extracted from multipart field `image`
sed -n '1,220p' apps/meteor/app/api/server/lib/getUploadFormData.ts
# Find client-side creation of AvatarServiceObject (blob/service) candidates
rg -n "AvatarServiceObject|contentType:|setAvatarFromService|users\.setAvatar|useUpdateAvatar\\(" apps/meteor/client apps/meteor -S
# Search for FormData append patterns that use 'image' field
rg -n "append\\(['\"]image['\"]" apps/meteor/client -SRepository: RocketChat/Rocket.Chat
Length of output: 16661
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find where the avatar state is created/updated for AccountProfileForm/AdminUserForm and passes into useUpdateAvatar
rg -n "useUpdateAvatar\\(" apps/meteor/client/views apps/meteor/client/providers -S
rg -n "const \\[avatar\\]|let avatar|avatar\\s*=" apps/meteor/client -S -g'*.ts*' | head -n 80
# Find how AvatarObject is populated from service suggestions (UserAvatarSuggestion has blob: string)
rg -n "UserAvatarSuggestion" apps/meteor/client -S
rg -n "blob\\s*:\\s*.*contentType|contentType\\s*:" apps/meteor/client/components/avatar -S -g'*.ts*'
# Find conversions from data URI/string -> Blob for uploading
rg -n "atob\\(|new Blob\\(|data:.*;base64|fetch\\(.*data:" apps/meteor/client/components/avatar apps/meteor/client/hooks -S -g'*.ts*'
# Search for remaining setAvatarFromService usage on client
rg -n "setAvatarFromService" apps/meteor/client -SRepository: RocketChat/Rocket.Chat
Length of output: 12753
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Open forms where avatar state is passed to useUpdateAvatar
sed -n '1,220p' apps/meteor/client/views/account/profile/AccountProfileForm.tsx
sed -n '1,240p' apps/meteor/client/views/admin/users/AdminUserForm.tsx
# Inspect avatar editor that likely builds AvatarObject (reset/url/service/upload)
sed -n '1,260p' apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx
# Inspect suggestion type/source to see what `blob: string` represents
sed -n '1,220p' apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestion.ts
sed -n '1,260p' apps/meteor/client/components/avatar/UserAvatarEditor/useUserAvatarSuggestions.ts
# Search for conversions that turn suggestion/blob string into Blob
rg -n "new Blob\\(|data:|atob\\(|Buffer\\.from\\(|fetch\\(|Blob\\(" apps/meteor/client/components/avatar/UserAvatarEditor -S
# Search for places that produce `AvatarServiceObject` (or `{ blob, contentType, service }`) on client
rg -n "blob\\s*:\\s*.*contentType\\s*:\\s*.*service\\s*:" apps/meteor/client -S
rg -n "AvatarServiceObject|isServiceObject\\(" apps/meteor/client -SRepository: RocketChat/Rocket.Chat
Length of output: 21098
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect getAvatarSuggestionForUser to see what `blob` contains (base64/data URI/etc)
sed -n '130,240p' apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts
# Inspect users.getAvatarSuggestion endpoint to confirm response mapping
rg -n "users\\.getAvatarSuggestion|getAvatarSuggestionForUser" apps/meteor/app/api/server -S
rg -n "getAvatarSuggestion" apps/meteor/app/api/server/v1/users.ts -S
sed -n '1,260p' apps/meteor/app/api/server/v1/users.ts | rg -n "getAvatarSuggestion|AvatarSuggestion|users\\.getAvatarSuggestion" -nRepository: RocketChat/Rocket.Chat
Length of output: 2091
Fix avatar upload for avatar suggestions: blob is a data-URI string but is appended as a multipart “file”
useUpdateAvatar treats any non-reset object with service as an AvatarServiceObject and does formData.append('image', blob). However, AvatarServiceObject.blob is a Blob, while UserAvatarSuggestion.blob is constructed server-side as a data:<content-type>;base64,... string and then cast into AvatarObject. Appending that string to FormData won’t create a multipart file part for the image field, and users.setAvatar uses getUploadFormData(..., { field: 'image' }) which requires an uploaded file—so the avatar update can fail for suggestions.
Relevant code
const { blob, service } = avatarObj;
const formData = new FormData();
formData.append('userId', userId);
formData.append('service', service);
formData.append('image', blob);
await saveAvatarAction(formData);Convert suggestion blob (data URI string) to a Blob before appending, or tighten the isServiceObject guard to require blob is actually a Blob at runtime and route otherwise.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/meteor/client/hooks/useUpdateAvatar.ts` around lines 56 - 61, The avatar
upload is appending a data-URI string as a file which fails multipart handling
in saveAvatarAction/users.setAvatar; in useUpdateAvatar detect when
avatarObj.blob is a string (UserAvatarSuggestion data:<type>;base64,...) and
convert that data URI into a real Blob (decode base64, create Uint8Array/Blob
with the correct MIME type and optional filename) before calling
formData.append('image', blob); alternatively tighten the isServiceObject guard
to require avatarObj.blob instanceof Blob and handle string blobs by converting
them to Blob or routing to the suggestion upload path; ensure
saveAvatarAction/getUploadFormData still receives a real File/Blob for the
'image' field.
There was a problem hiding this comment.
8 issues found across 42 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/meteor/client/hooks/useUpdateAvatar.ts">
<violation number="1" location="apps/meteor/client/hooks/useUpdateAvatar.ts:60">
P2: Service-avatar uploads can send non-binary image data because `blob` may be a string; convert it to a real `Blob` before appending to `FormData`.</violation>
</file>
<file name="apps/meteor/server/methods/logoutCleanUp.ts">
<violation number="1" location="apps/meteor/server/methods/logoutCleanUp.ts:21">
P2: Deprecated `logoutCleanUp` still triggers logout side effects, which can duplicate cleanup/event execution now that the same hook also runs from `Accounts.onLogout`.</violation>
</file>
<file name="apps/meteor/app/api/server/v1/cloud.ts">
<violation number="1" location="apps/meteor/app/api/server/v1/cloud.ts:306">
P1: The new `cloud.connectWorkspace` route ignores the boolean result of `connectWorkspace` and always returns success, so failed workspace connections are incorrectly reported as successful.</violation>
</file>
<file name="apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx">
<violation number="1" location="apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx:56">
P1: This call no longer checks whether workspace connection actually succeeded, so failures can be reported as successful connections.</violation>
</file>
<file name="apps/meteor/app/api/server/v1/permissions.ts">
<violation number="1" location="apps/meteor/app/api/server/v1/permissions.ts:210">
P2: Add a route-level `permissionsRequired` gate for `permissions.addRole`. Without it, authorization failures are handled inside the helper and fall back to 400 instead of a consistent early 403.
(Based on your team's feedback about permission checks at public API entry points.) [FEEDBACK_USED]</violation>
<violation number="2" location="apps/meteor/app/api/server/v1/permissions.ts:228">
P2: Add a route-level `permissionsRequired` gate for `permissions.removeRole`. This keeps permission failures on the middleware path (403) instead of relying on helper-thrown errors that are returned as 400.
(Based on your team's feedback about permission checks at public API entry points.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/meteor/app/authorization/server/functions/permissionRole.ts">
<violation number="1" location="apps/meteor/app/authorization/server/functions/permissionRole.ts:46">
P2: Validate the role exists before writing it to permission roles, otherwise invalid role IDs can be persisted.</violation>
</file>
<file name="apps/meteor/app/api/server/v1/users.ts">
<violation number="1" location="apps/meteor/app/api/server/v1/users.ts:2121">
P2: The user lookup should occur after `verifyEmail` succeeds. If `findOne` returns `null` due to a race or replication lag but `verifyEmail` still succeeds (it removes the token from the document), `runAfterVerifyEmail` will be silently skipped—leaving the anonymous→user role swap incomplete. Move the lookup after the `callAsync` or re-fetch the user post-verification.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| await connectWorkspace(this.bodyParams.token); | ||
| return API.v1.success(); |
There was a problem hiding this comment.
P1: The new cloud.connectWorkspace route ignores the boolean result of connectWorkspace and always returns success, so failed workspace connections are incorrectly reported as successful.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/cloud.ts, line 306:
<comment>The new `cloud.connectWorkspace` route ignores the boolean result of `connectWorkspace` and always returns success, so failed workspace connections are incorrectly reported as successful.</comment>
<file context>
@@ -287,6 +289,25 @@ declare module '@rocket.chat/rest-typings' {
+ },
+ },
+ async function action() {
+ await connectWorkspace(this.bodyParams.token);
+ return API.v1.success();
+ },
</file context>
| await connectWorkspace(this.bodyParams.token); | |
| return API.v1.success(); | |
| const connected = await connectWorkspace(this.bodyParams.token); | |
| if (!connected) { | |
| return API.v1.failure('Failed to connect workspace'); | |
| } | |
| return API.v1.success(); |
| if (!isConnected) { | ||
| throw Error(t('RegisterWorkspace_Connection_Error')); | ||
| } | ||
| await connectWorkspace({ token }); |
There was a problem hiding this comment.
P1: This call no longer checks whether workspace connection actually succeeded, so failures can be reported as successful connections.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx, line 56:
<comment>This call no longer checks whether workspace connection actually succeeded, so failures can be reported as successful connections.</comment>
<file context>
@@ -53,11 +53,7 @@ const RegisterWorkspaceTokenModal = ({ onClose, onStatusChange, ...props }: Regi
- if (!isConnected) {
- throw Error(t('RegisterWorkspace_Connection_Error'));
- }
+ await connectWorkspace({ token });
setModal(null);
</file context>
| const formData = new FormData(); | ||
| formData.append('userId', userId); | ||
| formData.append('service', service); | ||
| formData.append('image', blob); |
There was a problem hiding this comment.
P2: Service-avatar uploads can send non-binary image data because blob may be a string; convert it to a real Blob before appending to FormData.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/hooks/useUpdateAvatar.ts, line 60:
<comment>Service-avatar uploads can send non-binary image data because `blob` may be a string; convert it to a real `Blob` before appending to `FormData`.</comment>
<file context>
@@ -54,30 +53,19 @@ export const useUpdateAvatar = (avatarObj: AvatarObject, userId: IUser['_id']) =
+ const formData = new FormData();
+ formData.append('userId', userId);
+ formData.append('service', service);
+ formData.append('image', blob);
+ await saveAvatarAction(formData);
return;
</file context>
|
|
||
| // App IPostUserLogout event hook | ||
| await Apps.self?.triggerEvent(AppEvents.IPostUserLoggedOut, user); | ||
| await runUserLogoutCleanUp(user); |
There was a problem hiding this comment.
P2: Deprecated logoutCleanUp still triggers logout side effects, which can duplicate cleanup/event execution now that the same hook also runs from Accounts.onLogout.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/server/methods/logoutCleanUp.ts, line 21:
<comment>Deprecated `logoutCleanUp` still triggers logout side effects, which can duplicate cleanup/event execution now that the same hook also runs from `Accounts.onLogout`.</comment>
<file context>
@@ -15,13 +15,9 @@ declare module '@rocket.chat/ddp-client' {
-
- // App IPostUserLogout event hook
- await Apps.self?.triggerEvent(AppEvents.IPostUserLoggedOut, user);
+ await runUserLogoutCleanUp(user);
},
});
</file context>
| await runUserLogoutCleanUp(user); | |
| return; |
| 'permissions.removeRole', | ||
| { | ||
| authRequired: true, | ||
| body: isPermissionRolePayload, |
There was a problem hiding this comment.
P2: Add a route-level permissionsRequired gate for permissions.removeRole. This keeps permission failures on the middleware path (403) instead of relying on helper-thrown errors that are returned as 400.
(Based on your team's feedback about permission checks at public API entry points.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/permissions.ts, line 228:
<comment>Add a route-level `permissionsRequired` gate for `permissions.removeRole`. This keeps permission failures on the middleware path (403) instead of relying on helper-thrown errors that are returned as 400.
(Based on your team's feedback about permission checks at public API entry points.) </comment>
<file context>
@@ -185,6 +205,42 @@ const permissionsEndpoints = API.v1
+ },
+ )
+ .post(
+ 'permissions.removeRole',
+ {
+ authRequired: true,
</file context>
| 'permissions.removeRole', | |
| { | |
| authRequired: true, | |
| body: isPermissionRolePayload, | |
| 'permissions.removeRole', | |
| { | |
| authRequired: true, | |
| permissionsRequired: ['access-permissions'], | |
| body: isPermissionRolePayload, |
| 'permissions.addRole', | ||
| { | ||
| authRequired: true, | ||
| body: isPermissionRolePayload, |
There was a problem hiding this comment.
P2: Add a route-level permissionsRequired gate for permissions.addRole. Without it, authorization failures are handled inside the helper and fall back to 400 instead of a consistent early 403.
(Based on your team's feedback about permission checks at public API entry points.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/permissions.ts, line 210:
<comment>Add a route-level `permissionsRequired` gate for `permissions.addRole`. Without it, authorization failures are handled inside the helper and fall back to 400 instead of a consistent early 403.
(Based on your team's feedback about permission checks at public API entry points.) </comment>
<file context>
@@ -185,6 +205,42 @@ const permissionsEndpoints = API.v1
},
+ )
+ .post(
+ 'permissions.addRole',
+ {
+ authRequired: true,
</file context>
| 'permissions.addRole', | |
| { | |
| authRequired: true, | |
| body: isPermissionRolePayload, | |
| 'permissions.addRole', | |
| { | |
| authRequired: true, | |
| permissionsRequired: ['access-permissions'], | |
| body: isPermissionRolePayload, |
| void notifyOnPermissionChangedById(permission.groupPermissionId); | ||
| } | ||
|
|
||
| await Permissions.addRole(permission._id, role); |
There was a problem hiding this comment.
P2: Validate the role exists before writing it to permission roles, otherwise invalid role IDs can be persisted.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/authorization/server/functions/permissionRole.ts, line 46:
<comment>Validate the role exists before writing it to permission roles, otherwise invalid role IDs can be persisted.</comment>
<file context>
@@ -0,0 +1,78 @@
+ void notifyOnPermissionChangedById(permission.groupPermissionId);
+ }
+
+ await Permissions.addRole(permission._id, role);
+
+ void notifyOnPermissionChangedById(permission._id);
</file context>
| async function action() { | ||
| const { token } = this.bodyParams; | ||
|
|
||
| const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } }); |
There was a problem hiding this comment.
P2: The user lookup should occur after verifyEmail succeeds. If findOne returns null due to a race or replication lag but verifyEmail still succeeds (it removes the token from the document), runAfterVerifyEmail will be silently skipped—leaving the anonymous→user role swap incomplete. Move the lookup after the callAsync or re-fetch the user post-verification.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/users.ts, line 2121:
<comment>The user lookup should occur after `verifyEmail` succeeds. If `findOne` returns `null` due to a race or replication lag but `verifyEmail` still succeeds (it removes the token from the document), `runAfterVerifyEmail` will be silently skipped—leaving the anonymous→user role swap incomplete. Move the lookup after the `callAsync` or re-fetch the user post-verification.</comment>
<file context>
@@ -2088,6 +2098,38 @@ API.v1
+ async function action() {
+ const { token } = this.bodyParams;
+
+ const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });
+
+ await Meteor.callAsync('verifyEmail', token);
</file context>
| async function action() { | ||
| const { token } = this.bodyParams; | ||
|
|
||
| const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } }); | ||
|
|
||
| await Meteor.callAsync('verifyEmail', token); | ||
|
|
||
| if (user) { | ||
| await runAfterVerifyEmail(user._id); | ||
| } | ||
|
|
||
| return API.v1.success(); | ||
| }, |
There was a problem hiding this comment.
🔴 High: Missing authorization check in users.update API allows account takeover and privilege escalation
The users.update API endpoint does not enforce sufficient authorization checks before calling saveUser to update user information. While it requires 2FA, it lacks explicit permission requirements (e.g., edit-other-user-info), potentially allowing authenticated users to modify the profiles of other users.
Trace
graph TD
subgraph SG0 ["apps/meteor/app/2fa/server/code/index.ts"]
getUserForCheck["Retrieves user data required for two-factor authentication checks."]
end
style SG0 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG1 ["apps/meteor/app/2fa/server/functions/resetTOTP.ts"]
sendResetNotification_2["sendResetNotification"]
resetTOTP["Resets a user's TOTP configuration, optionally notifying the user via email."]
end
style SG1 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG2 ["apps/meteor/app/api/server/ApiClass.ts"]
APIClass.success["Returns a successful API response."]
APIClass.failure["Returns a failure API response."]
APIClass.forbidden["Returns a 403 Forbidden API response."]
APIClass.this.parseJsonQuery["Parses JSON query parameters for an API context."]
end
style SG2 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG3 ["apps/meteor/app/api/server/helpers/getPaginationItems.ts"]
getPaginationItems["Calculates pagination offset and count based on request parameters and system settings."]
end
style SG3 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG4 ["apps/meteor/app/api/server/helpers/getUserFromParams.ts"]
getUserFromParams["Retrieves a user object based on userId or username parameters from an API request."]
end
style SG4 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG5 ["apps/meteor/app/api/server/helpers/getUserInfo.ts"]
isVerifiedEmail["isVerifiedEmail"]
getUserPreferences["getUserPreferences"]
filterOutdatedVersionUpdateBanners["filterOutdatedVersionUpdateBanners"]
getUserCalendar["getUserCalendar"]
getUserInfo["Constructs and returns a comprehensive user information object for API responses, including preferences and calendar settings."]
end
style SG5 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG6 ["apps/meteor/app/api/server/helpers/isUserFromParams.ts"]
isUserFromParams["Validates if the provided parameters identify the currently logged-in user."]
end
style SG6 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG7 ["apps/meteor/app/api/server/lib/eraseTeam.ts"]
eraseTeamOnRelinquishRoomOwnerships["eraseTeamOnRelinquishRoomOwnerships"]
eraseRoomLooseValidation["eraseRoomLooseValidation"]
end
style SG7 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG8 ["apps/meteor/app/api/server/lib/getUploadFormData.ts"]
getUploadFormData["Parses multipart/form-data from an HTTP request to extract file data and fields."]
end
style SG8 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG9 ["apps/meteor/app/api/server/lib/isValidQuery.ts"]
._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts["Top-level logic providing a utility to validate query objects against allowed attributes and operations."]
end
style SG9 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG10 ["apps/meteor/app/api/server/lib/users.ts"]
findUsersToAutocomplete["Provides autocomplete suggestions for users based on search terms and access permissions."]
getInclusiveFields["Filters a query object to retain only inclusive fields."]
getNonEmptyFields["Returns default fields if the provided fields object is empty or invalid."]
getNonEmptyQuery["Returns a default query if the provided query object is empty, optionally including email searches."]
findPaginatedUsersByStatus["Retrieves a paginated list of users filtered by status, roles, and other criteria."]
end
style SG10 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG11 ["apps/meteor/app/api/server/v1/users.ts"]
get["get"]
action{{"action"}}
end
style SG11 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG12 ["apps/meteor/app/authentication/server/startup/index.js"]
Accounts.insertUserDoc["Accounts.insertUserDoc"]
end
style SG12 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG13 ["apps/meteor/app/authorization/server/functions/hasPermission.ts"]
hasPermissionAsync["Checks if a user has a specific permission, optionally within a room scope."]
end
style SG13 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG14 ["apps/meteor/app/authorization/server/index.ts"]
._Rocket.Chat_apps_meteor_app_authorization_server_index.ts["Exports authorization-related functions and registers server methods."]
end
style SG14 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG15 ["apps/meteor/app/file-upload/server/lib/FileUpload.ts"]
FileUpload.getStore["Retrieves a file storage handler by model name."]
FileUpload.getStoreByName["FileUpload.getStoreByName"]
FileUpload.get["FileUpload.get"]
FileUpload.removeFilesByRoomId["FileUpload.removeFilesByRoomId"]
FileUploadClass.deleteById["FileUploadClass.deleteById"]
end
style SG15 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG16 ["apps/meteor/app/file/server/file.server.ts"]
RocketChatFile.dataURIParse["RocketChatFile.dataURIParse"]
end
style SG16 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG17 ["apps/meteor/app/invites/server/functions/validateInviteToken.ts"]
validateInviteToken["validateInviteToken"]
end
style SG17 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG18 ["apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts"]
addUserToDefaultChannels["addUserToDefaultChannels"]
end
style SG18 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG19 ["apps/meteor/app/lib/server/functions/addUserToRoom.ts"]
addUserToRoom["addUserToRoom"]
end
style SG19 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG20 ["apps/meteor/app/lib/server/functions/checkEmailAvailability.ts"]
checkEmailAvailability["Checks if an email address is available in the database."]
end
style SG20 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG21 ["apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts"]
toRegExp["toRegExp"]
usernameIsBlocked["usernameIsBlocked"]
checkUsernameAvailabilityWithValidation["Checks if a username is available with validation logic for user editing."]
checkUsernameAvailability["Checks if a username is available in the system, considering blocked lists and existing users/teams."]
end
style SG21 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG22 ["apps/meteor/app/lib/server/functions/deleteRoom.ts"]
deleteRoom["deleteRoom"]
end
style SG22 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG23 ["apps/meteor/app/lib/server/functions/deleteUser.ts"]
deleteUser["Deletes a user account, cleans up associated data (messages, subscriptions, files, roles), and triggers cleanup callbacks."]
end
style SG23 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG24 ["apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts"]
getAvatarSuggestionForUser["Fetches and processes potential user avatar URLs from various social/OAuth providers and Gravatar."]
end
style SG24 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG25 ["apps/meteor/app/lib/server/functions/getDefaultChannels.ts"]
getDefaultChannels["getDefaultChannels"]
end
style SG25 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG26 ["apps/meteor/app/lib/server/functions/getFullUserData.ts"]
getCustomFields["getCustomFields"]
getFields["getFields"]
findTargetUser["findTargetUser"]
getFullUserDataByUniqueSearchTerm["Retrieves a full user object based on a unique search term, enforcing authorization checks and filtering sensitive fields."]
end
style SG26 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG27 ["apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts"]
shouldRemoveOrChangeOwner["shouldRemoveOrChangeOwner"]
getSubscribedRoomsForUserWithDetails["getSubscribedRoomsForUserWithDetails"]
end
style SG27 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG28 ["apps/meteor/app/lib/server/functions/getUserSingleOwnedRooms.ts"]
getUserSingleOwnedRooms["getUserSingleOwnedRooms"]
end
style SG28 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG29 ["apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts"]
slug["slug"]
usernameIsAvailable["usernameIsAvailable"]
name["name"]
generateUsernameSuggestion["Generates a unique username suggestion for a user based on their name, email, or OAuth services."]
end
style SG29 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG30 ["apps/meteor/app/lib/server/functions/joinDefaultChannels.ts"]
joinDefaultChannels["joinDefaultChannels"]
end
style SG30 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG31 ["apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts"]
bulkTeamCleanup["bulkTeamCleanup"]
bulkRoomCleanUp["bulkRoomCleanUp"]
relinquishRoomOwnerships["relinquishRoomOwnerships"]
end
style SG31 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG32 ["apps/meteor/app/lib/server/functions/saveCustomFields.ts"]
saveCustomFields["Validates and saves custom user profile fields to the database."]
end
style SG32 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG33 ["apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts"]
getCustomFieldsMeta["getCustomFieldsMeta"]
saveCustomFieldsWithoutValidation["Saves custom user fields to the database without performing additional validation."]
end
style SG33 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG34 ["apps/meteor/app/lib/server/functions/saveUser/index.ts"]
._Rocket.Chat_apps_meteor_app_lib_server_functions_saveUser_index.ts["Exports core user saving and validation functions for use in other modules."]
end
style SG34 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG35 ["apps/meteor/app/lib/server/functions/saveUser/sendUserEmail.ts"]
sendUserEmail["sendUserEmail"]
sendWelcomeEmail["Sends a welcome email to a new user upon registration."]
end
style SG35 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG36 ["apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts"]
canEditExtension["Validates if a specific VoIP extension can be assigned to a user."]
end
style SG36 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG37 ["apps/meteor/app/lib/server/functions/saveUserIdentity.ts"]
saveUserIdentity["saveUserIdentity"]
updateUsernameReferences["updateUsernameReferences"]
end
style SG37 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG38 ["apps/meteor/app/lib/server/functions/setRealName.ts"]
setRealName["setRealName"]
end
style SG38 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG39 ["apps/meteor/app/lib/server/functions/setStatusText.ts"]
setStatusText["Updates a user's status text, truncates it to 120 characters, persists to the database, and broadcasts the update."]
end
style SG39 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG40 ["apps/meteor/app/lib/server/functions/setUserAvatar.ts"]
setAvatarFromServiceWithValidation["setAvatarFromServiceWithValidation"]
setUserAvatar["Sets a user's avatar from a data URI, URL, or REST input, handling file storage and database updates."]
end
style SG40 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG41 ["apps/meteor/app/lib/server/functions/setUsername.ts"]
isUserInFederatedRooms["isUserInFederatedRooms"]
setUsernameWithValidation["Validates and updates a user's username, ensuring uniqueness and compliance with federation rules."]
setUsername["_setUsername"]
end
style SG41 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG42 ["apps/meteor/app/lib/server/functions/updateGroupDMsName.ts"]
getFname["getFname"]
getName["getName"]
getUsersWhoAreInTheSameGroupDMsAs["getUsersWhoAreInTheSameGroupDMsAs"]
updateGroupDMsName["updateGroupDMsName"]
getMembers["getMembers"]
end
style SG42 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG43 ["apps/meteor/app/lib/server/functions/validateCustomFields.js"]
validateCustomFields["Validates user-defined custom fields against configured schema, ensuring required fields, types, and lengths are respected."]
end
style SG43 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG44 ["apps/meteor/app/lib/server/functions/validateName.ts"]
validateName["validateName"]
end
style SG44 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG45 ["apps/meteor/app/lib/server/functions/validateNameChars.ts"]
validateNameChars["Validates a name string for invalid characters, including URI-decoded checks."]
end
style SG45 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG46 ["apps/meteor/app/lib/server/functions/validateUsername.ts"]
validateUsername["Validates a username string against a configurable regular expression pattern."]
end
style SG46 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG47 ["apps/meteor/app/lib/server/index.ts"]
._Rocket.Chat_apps_meteor_app_lib_server_index.ts["./Rocket.Chat/apps/meteor/app/lib/server/index.ts"]
end
style SG47 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG48 ["apps/meteor/app/lib/server/lib/deprecationWarningLogger.ts"]
compareVersions["Compares a version string against the threshold for throwing deprecation errors."]
method["Logs deprecation warnings for methods, including replacement information and metrics."]
warn["Logs a generic deprecation warning."]
end
style SG48 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG49 ["apps/meteor/app/lib/server/lib/notifyListener.ts"]
notifyOnRoomChangedById["notifyOnRoomChangedById"]
notifyOnRoomChangedByUsernamesOrUids["notifyOnRoomChangedByUsernamesOrUids"]
notifyOnIntegrationChangedByUserId["notifyOnIntegrationChangedByUserId"]
notifyOnLivechatDepartmentAgentChanged["notifyOnLivechatDepartmentAgentChanged"]
notifyOnSettingChanged["notifyOnSettingChanged"]
notifyOnSettingChangedById["notifyOnSettingChangedById"]
notifyOnUserChange["Broadcasts a notification about a user record change."]
notifyOnUserChangeAsync["Executes a callback and notifies on user change if watchers are disabled."]
notifyOnSubscriptionChanged["Broadcasts a notification when a subscription is changed."]
notifyOnSubscriptionChangedByRoomIdAndUserId["Broadcasts a notification for a subscription identified by room ID and user ID."]
notifyOnSubscriptionChangedById["notifyOnSubscriptionChangedById"]
notifyOnSubscriptionChangedByUserPreferences["notifyOnSubscriptionChangedByUserPreferences"]
notifyOnSubscriptionChangedByRoomId["notifyOnSubscriptionChangedByRoomId"]
notifyOnSubscriptionChangedByAutoTranslateAndUserId["notifyOnSubscriptionChangedByAutoTranslateAndUserId"]
notifyOnSubscriptionChangedByUserIdAndRoomType["notifyOnSubscriptionChangedByUserIdAndRoomType"]
notifyOnSubscriptionChangedByNameAndRoomType["notifyOnSubscriptionChangedByNameAndRoomType"]
notifyOnSubscriptionChangedByUserId["notifyOnSubscriptionChangedByUserId"]
end
style SG49 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG50 ["apps/meteor/app/lib/server/methods/createToken.ts"]
generateAccessToken["Generates an access token for a given user if the provided secret matches the environment variable."]
end
style SG50 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG51 ["apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts"]
deleteUserOwnAccount["Handles the deletion of a user's own account after verifying their password and system settings."]
end
style SG51 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG52 ["apps/meteor/app/mailer/server/api.ts"]
replacekey["replacekey"]
translate["translate"]
replace["replace"]
replaceEscaped["replaceEscaped"]
wrap["wrap"]
checkAddressFormat["checkAddressFormat"]
sendNoWrap["sendNoWrap"]
send["send"]
end
style SG52 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG53 ["apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts"]
getDefaultSubscriptionPref["getDefaultSubscriptionPref"]
end
style SG53 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG54 ["apps/meteor/app/utils/lib/getURL.ts"]
getCloudUrl["getCloudUrl"]
getURL["_getURL"]
getURLWithoutSettings["getURLWithoutSettings"]
end
style SG54 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG55 ["apps/meteor/app/utils/lib/mimeTypes.ts"]
getMimeTypeFromFileName["getMimeTypeFromFileName"]
getMimeType["getMimeType"]
end
style SG55 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG56 ["apps/meteor/app/utils/server/functions/isSMTPConfigured.ts"]
isSMTPConfigured["Checks if SMTP is configured by verifying the MAIL_URL environment variable or SMTP_Host setting."]
end
style SG56 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG57 ["apps/meteor/app/utils/server/getURL.ts"]
getURL_2["Generates a URL for a given path, incorporating site URL and CDN settings."]
end
style SG57 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG58 ["apps/meteor/app/utils/server/lib/getUserPreference.ts"]
getUserPreference["getUserPreference"]
end
style SG58 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG59 ["apps/meteor/client/meteor/overrides/userAndUsers.ts"]
Meteor.userId["Overrides Meteor.userId to return the current user ID from the local store."]
end
style SG59 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG60 ["apps/meteor/client/meteor/user.ts"]
watchUserId["Watches and returns the current user ID."]
end
style SG60 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG61 ["apps/meteor/client/meteor/watch.ts"]
watch["watch"]
end
style SG61 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG62 ["apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts"]
generatePersonalAccessTokenOfUser["Generates a new personal access token for a user after checking permissions and ensuring uniqueness."]
end
style SG62 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG63 ["apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts"]
regeneratePersonalAccessTokenOfUser["Regenerates a personal access token for a user after verifying authorization and token existence."]
end
style SG63 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG64 ["apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts"]
removePersonalAccessTokenOfUser["Removes a personal access token for a user after verifying authorization."]
end
style SG64 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG65 ["apps/meteor/lib/roles/calculateRoomRolePriorityFromRoles.ts"]
getRoomRolePriorityForRole["getRoomRolePriorityForRole"]
calculateRoomRolePriorityFromRoles["calculateRoomRolePriorityFromRoles"]
end
style SG65 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG66 ["apps/meteor/lib/utils/isObject.ts"]
isObject["isObject"]
end
style SG66 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG67 ["apps/meteor/lib/utils/parseCSV.ts"]
parseCSV["parseCSV"]
end
style SG67 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG68 ["apps/meteor/lib/utils/stringUtils.ts"]
makeString["makeString"]
defaultToWhiteSpace["defaultToWhiteSpace"]
trim["trim"]
ltrim["ltrim"]
rtrim["rtrim"]
strLeft["strLeft"]
strRightBack["strRightBack"]
end
style SG68 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG69 ["apps/meteor/server/database/utils.ts"]
isExtendedSession["isExtendedSession"]
onceTransactionCommitedSuccessfully["onceTransactionCommitedSuccessfully"]
withError["withError"]
end
style SG69 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG70 ["apps/meteor/server/hooks/userLogoutCleanUp.ts"]
runUserLogoutCleanUp["Triggers cleanup operations after a user logs out."]
end
style SG70 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG71 ["apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts"]
getSubscriptionAutotranslateDefaultConfig["getSubscriptionAutotranslateDefaultConfig"]
end
style SG71 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG72 ["apps/meteor/server/lib/isUserIdFederated.ts"]
isUserIdFederated["isUserIdFederated"]
end
style SG72 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG73 ["apps/meteor/server/lib/resetUserE2EKey.ts"]
sendResetNotification["sendResetNotification"]
resetUserE2EEncriptionKey["Resets a user's E2E encryption key and forces a re-login."]
end
style SG73 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG74 ["apps/meteor/server/lib/roles/addUserRoles.ts"]
addUserRolesAsync["Adds roles to a user, optionally scoped to a specific room."]
end
style SG74 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG75 ["apps/meteor/server/lib/roles/removeUserFromRoles.ts"]
removeUserFromRolesAsync["Removes specified roles from a user, optionally scoped to a room."]
end
style SG75 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG76 ["apps/meteor/server/lib/roles/syncRoomRolePriority.ts"]
syncRoomRolePriorityForUserAndRoom["Synchronizes the room role priority for a user in a specific room."]
updateRolePriority["updateRolePriority"]
end
style SG76 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG77 ["apps/meteor/server/lib/roles/validateRoleList.ts"]
validateRoleList["Validates a list of role IDs against existing roles in the database."]
end
style SG77 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG78 ["apps/meteor/server/lib/rooms/roomCoordinator.ts"]
RoomCoordinatorServer.allowMemberAction["RoomCoordinatorServer.allowMemberAction"]
RoomCoordinatorServer.getRoomDirectives["Retrieves the server-side directives for a specific room type."]
end
style SG78 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG79 ["apps/meteor/server/lib/users/runAfterVerifyEmail.ts"]
runAfterVerifyEmail["Updates user roles after email verification."]
end
style SG79 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG80 ["apps/meteor/server/methods/registerUser.ts"]
registerUser["Registers a new user, handles anonymous access, and validates registration requirements."]
end
style SG80 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG81 ["apps/meteor/server/methods/requestDataDownload.ts"]
requestDataDownload["Requests a data export operation for the user."]
end
style SG81 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG82 ["apps/meteor/server/methods/resetAvatar.ts"]
resetAvatar["Resets a user's avatar after authorization checks."]
userId["userId"]
end
style SG82 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG83 ["apps/meteor/server/methods/saveUserPreferences.ts"]
updateNotificationPreferences["updateNotificationPreferences"]
saveUserPreferences["Updates user preferences and propagates notification changes."]
end
style SG83 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG84 ["apps/meteor/server/methods/sendConfirmationEmail.ts"]
sendConfirmationEmail["Sends a verification email to a user based on their email address."]
end
style SG84 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG85 ["apps/meteor/server/methods/sendForgotPasswordEmail.ts"]
sendForgotPasswordEmail["Sends a password reset email to a user if they exist."]
end
style SG85 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG86 ["apps/meteor/server/methods/setUserActiveStatus.ts"]
executeSetUserActiveStatus["Updates the active status of a user, requiring administrative permissions."]
setUserActiveStatus["setUserActiveStatus"]
end
style SG86 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG87 ["apps/meteor/server/services/authorization/service.ts"]
Authorization.hasPermission["Checks if a user has a specific permission, optionally scoped to a room."]
Authorization.all["Authorization.all"]
end
style SG87 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG88 ["apps/meteor/server/services/user/lib/getNewUserRoles.ts"]
getNewUserRoles["getNewUserRoles"]
end
style SG88 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG89 ["ee/apps/account-service/src/lib/utils.ts"]
generateStampedLoginToken["_generateStampedLoginToken"]
hashLoginToken["Hashes a login token using SHA-256 for secure storage."]
end
style SG89 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG90 ["packages/models/src/models/BaseRaw.ts"]
BaseRaw.doNotMixInclusionAndExclusionFields["BaseRaw.doNotMixInclusionAndExclusionFields"]
BaseRaw.ensureDefaultFields["BaseRaw.ensureDefaultFields"]
BaseRaw.find["BaseRaw.find"]
BaseRaw.updateOne["BaseRaw.updateOne"]
BaseRaw.updateMany["BaseRaw.updateMany"]
BaseRaw.deleteMany["BaseRaw.deleteMany"]
BaseRaw.setUpdatedAt["BaseRaw.setUpdatedAt"]
end
style SG90 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG91 ["packages/models/src/models/LivechatDepartmentAgents.ts"]
LivechatDepartmentAgentsRaw.findByAgentId["LivechatDepartmentAgentsRaw.findByAgentId"]
LivechatDepartmentAgentsRaw.removeByAgentId["LivechatDepartmentAgentsRaw.removeByAgentId"]
LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId["LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId"]
end
style SG91 fill:#2a2a2a,stroke:#444,color:#aaa
action --> hashLoginToken
action --> getFullUserDataByUniqueSearchTerm
action --> deleteUser
action --> generateUsernameSuggestion
action --> validateCustomFields
action --> saveCustomFields
action --> getAvatarSuggestionForUser
action --> checkUsernameAvailabilityWithValidation
action --> checkUsernameAvailability
action --> validateUsername
action --> validateNameChars
action --> saveCustomFieldsWithoutValidation
action --> canEditExtension
action --> ._Rocket.Chat_apps_meteor_app_lib_server_functions_saveUser_index.ts
action --> sendWelcomeEmail
action --> setUserAvatar
action --> setStatusText
action --> checkEmailAvailability
action --> setUsernameWithValidation
action --> notifyOnUserChange
action --> notifyOnUserChangeAsync
action --> deleteUserOwnAccount
action --> generateAccessToken
action --> resetUserE2EEncriptionKey
action --> sendForgotPasswordEmail
action --> executeSetUserActiveStatus
action --> resetAvatar
action --> sendConfirmationEmail
action --> requestDataDownload
action --> registerUser
action --> saveUserPreferences
action --> removePersonalAccessTokenOfUser
action --> generatePersonalAccessTokenOfUser
action --> regeneratePersonalAccessTokenOfUser
action --> resetTOTP
action --> getUserForCheck
action --> isSMTPConfigured
action --> hasPermissionAsync
action --> findUsersToAutocomplete
action --> findPaginatedUsersByStatus
action --> ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts
action --> getUploadFormData
action --> APIClass.success
action --> APIClass.failure
action --> APIClass.forbidden
action --> APIClass.this.parseJsonQuery
action --> getUserInfo
action --> getPaginationItems
action --> getUserFromParams
action --> isUserFromParams
action --> runUserLogoutCleanUp
action --> runAfterVerifyEmail
action --> get
getFullUserDataByUniqueSearchTerm --> getFields
getFullUserDataByUniqueSearchTerm --> findTargetUser
getFullUserDataByUniqueSearchTerm --> hasPermissionAsync
deleteUser --> shouldRemoveOrChangeOwner
deleteUser --> getSubscribedRoomsForUserWithDetails
deleteUser --> getUserSingleOwnedRooms
deleteUser --> updateGroupDMsName
deleteUser --> relinquishRoomOwnerships
deleteUser --> notifyOnRoomChangedById
deleteUser --> notifyOnIntegrationChangedByUserId
deleteUser --> notifyOnLivechatDepartmentAgentChanged
deleteUser --> notifyOnUserChange
deleteUser --> FileUpload.getStore
deleteUser --> LivechatDepartmentAgentsRaw.findByAgentId
deleteUser --> LivechatDepartmentAgentsRaw.removeByAgentId
generateUsernameSuggestion --> slug
generateUsernameSuggestion --> usernameIsAvailable
generateUsernameSuggestion --> name
validateCustomFields --> trim
saveCustomFields --> validateCustomFields
saveCustomFields --> saveCustomFieldsWithoutValidation
saveCustomFields --> trim
checkUsernameAvailabilityWithValidation --> checkUsernameAvailability
checkUsernameAvailability --> toRegExp
checkUsernameAvailability --> usernameIsBlocked
checkUsernameAvailability --> validateName
saveCustomFieldsWithoutValidation --> getCustomFieldsMeta
saveCustomFieldsWithoutValidation --> notifyOnSubscriptionChangedByUserIdAndRoomType
saveCustomFieldsWithoutValidation --> onceTransactionCommitedSuccessfully
saveCustomFieldsWithoutValidation --> trim
sendWelcomeEmail --> sendUserEmail
setUserAvatar --> onceTransactionCommitedSuccessfully
setUserAvatar --> FileUpload.getStore
setUserAvatar --> RocketChatFile.dataURIParse
setStatusText --> onceTransactionCommitedSuccessfully
setUsernameWithValidation --> checkUsernameAvailability
setUsernameWithValidation --> saveUserIdentity
setUsernameWithValidation --> validateUsername
setUsernameWithValidation --> joinDefaultChannels
setUsernameWithValidation --> isUserInFederatedRooms
setUsernameWithValidation --> notifyOnUserChange
notifyOnUserChangeAsync --> notifyOnUserChange
deleteUserOwnAccount --> deleteUser
deleteUserOwnAccount --> method
deleteUserOwnAccount --> deleteUserOwnAccount
deleteUserOwnAccount --> trim
deleteUserOwnAccount --> Meteor.userId
generateAccessToken --> generateStampedLoginToken
resetUserE2EEncriptionKey --> isUserIdFederated
resetUserE2EEncriptionKey --> notifyOnUserChange
resetUserE2EEncriptionKey --> notifyOnSubscriptionChangedByUserId
resetUserE2EEncriptionKey --> sendResetNotification
sendForgotPasswordEmail --> sendForgotPasswordEmail
executeSetUserActiveStatus --> setUserActiveStatus
executeSetUserActiveStatus --> hasPermissionAsync
resetAvatar --> method
resetAvatar --> resetAvatar
resetAvatar --> userId
resetAvatar --> hasPermissionAsync
requestDataDownload --> method
requestDataDownload --> requestDataDownload
registerUser --> generateStampedLoginToken
registerUser --> ._Rocket.Chat_apps_meteor_app_lib_server_index.ts
registerUser --> Accounts.insertUserDoc
registerUser --> registerUser
registerUser --> validateInviteToken
registerUser --> trim
saveUserPreferences --> notifyOnUserChange
saveUserPreferences --> notifyOnSubscriptionChangedByAutoTranslateAndUserId
saveUserPreferences --> notifyOnSubscriptionChangedByUserId
saveUserPreferences --> method
saveUserPreferences --> updateNotificationPreferences
saveUserPreferences --> saveUserPreferences
saveUserPreferences --> Meteor.userId
removePersonalAccessTokenOfUser --> removePersonalAccessTokenOfUser
removePersonalAccessTokenOfUser --> hasPermissionAsync
generatePersonalAccessTokenOfUser --> hashLoginToken
generatePersonalAccessTokenOfUser --> hasPermissionAsync
regeneratePersonalAccessTokenOfUser --> removePersonalAccessTokenOfUser
regeneratePersonalAccessTokenOfUser --> generatePersonalAccessTokenOfUser
regeneratePersonalAccessTokenOfUser --> hasPermissionAsync
resetTOTP --> isUserIdFederated
resetTOTP --> notifyOnUserChange
resetTOTP --> sendResetNotification_2
hasPermissionAsync --> Authorization.hasPermission
findUsersToAutocomplete --> hasPermissionAsync
findPaginatedUsersByStatus --> hasPermissionAsync
getUploadFormData --> getMimeType
APIClass.success --> isObject
APIClass.failure --> isObject
APIClass.this.parseJsonQuery --> APIClass.this.parseJsonQuery
getUserInfo --> getURL_2
getUserInfo --> isVerifiedEmail
getUserInfo --> getUserPreferences
getUserInfo --> filterOutdatedVersionUpdateBanners
getUserInfo --> getUserCalendar
runAfterVerifyEmail --> removeUserFromRolesAsync
runAfterVerifyEmail --> addUserRolesAsync
get --> getURL_2
get --> hasPermissionAsync
get --> getInclusiveFields
get --> getNonEmptyFields
get --> getNonEmptyQuery
get --> ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts
get --> APIClass.success
get --> APIClass.forbidden
get --> APIClass.this.parseJsonQuery
get --> getPaginationItems
get --> getUserFromParams
get --> get
getFields --> getCustomFields
getSubscribedRoomsForUserWithDetails --> ._Rocket.Chat_apps_meteor_app_authorization_server_index.ts
updateGroupDMsName --> getFname
updateGroupDMsName --> getName
updateGroupDMsName --> getUsersWhoAreInTheSameGroupDMsAs
updateGroupDMsName --> getMembers
updateGroupDMsName --> notifyOnSubscriptionChangedByRoomId
relinquishRoomOwnerships --> bulkRoomCleanUp
relinquishRoomOwnerships --> addUserRolesAsync
FileUpload.getStore --> FileUpload.getStoreByName
FileUpload.getStore --> FileUpload.get
LivechatDepartmentAgentsRaw.findByAgentId --> BaseRaw.find
LivechatDepartmentAgentsRaw.removeByAgentId --> BaseRaw.deleteMany
name --> slug
trim --> makeString
trim --> defaultToWhiteSpace
onceTransactionCommitedSuccessfully --> isExtendedSession
onceTransactionCommitedSuccessfully --> withError
sendUserEmail --> send
saveUserIdentity --> updateUsernameReferences
saveUserIdentity --> validateName
saveUserIdentity --> setRealName
saveUserIdentity --> setUsername
saveUserIdentity --> onceTransactionCommitedSuccessfully
joinDefaultChannels --> addUserToDefaultChannels
method --> compareVersions
method --> warn
Meteor.userId --> watchUserId
sendResetNotification --> send
setUserActiveStatus --> executeSetUserActiveStatus
setUserActiveStatus --> Meteor.userId
Accounts.insertUserDoc --> getAvatarSuggestionForUser
Accounts.insertUserDoc --> setAvatarFromServiceWithValidation
Accounts.insertUserDoc --> joinDefaultChannels
Accounts.insertUserDoc --> notifyOnSettingChangedById
Accounts.insertUserDoc --> addUserRolesAsync
Accounts.insertUserDoc --> getNewUserRoles
Accounts.insertUserDoc --> parseCSV
updateNotificationPreferences --> notifyOnSubscriptionChangedByUserPreferences
sendResetNotification_2 --> send
Authorization.hasPermission --> Authorization.all
getMimeType --> getMimeTypeFromFileName
getURL_2 --> getURLWithoutSettings
getUserPreferences --> getUserPreference
removeUserFromRolesAsync --> notifyOnSubscriptionChangedByRoomIdAndUserId
removeUserFromRolesAsync --> syncRoomRolePriorityForUserAndRoom
removeUserFromRolesAsync --> validateRoleList
addUserRolesAsync --> notifyOnSubscriptionChangedByRoomIdAndUserId
addUserRolesAsync --> syncRoomRolePriorityForUserAndRoom
addUserRolesAsync --> validateRoleList
bulkRoomCleanUp --> bulkTeamCleanup
bulkRoomCleanUp --> notifyOnSubscriptionChanged
bulkRoomCleanUp --> FileUpload.removeFilesByRoomId
bulkRoomCleanUp --> eraseRoomLooseValidation
FileUpload.get --> FileUpload.getStoreByName
FileUpload.get --> FileUpload.get
BaseRaw.find --> BaseRaw.doNotMixInclusionAndExclusionFields
BaseRaw.find --> BaseRaw.find
BaseRaw.deleteMany --> BaseRaw.find
BaseRaw.deleteMany --> BaseRaw.updateOne
BaseRaw.deleteMany --> BaseRaw.deleteMany
defaultToWhiteSpace --> makeString
send --> replace
send --> wrap
send --> sendNoWrap
updateUsernameReferences --> updateGroupDMsName
updateUsernameReferences --> notifyOnRoomChangedByUsernamesOrUids
updateUsernameReferences --> notifyOnSubscriptionChangedByNameAndRoomType
updateUsernameReferences --> notifyOnSubscriptionChangedByUserId
updateUsernameReferences --> FileUpload.getStore
updateUsernameReferences --> LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId
setRealName --> onceTransactionCommitedSuccessfully
setUsername --> addUserToRoom
setUsername --> getAvatarSuggestionForUser
setUsername --> checkUsernameAvailability
setUsername --> validateUsername
setUsername --> setUserAvatar
setUsername --> isUserInFederatedRooms
setUsername --> onceTransactionCommitedSuccessfully
addUserToDefaultChannels --> getDefaultChannels
addUserToDefaultChannels --> notifyOnSubscriptionChangedById
addUserToDefaultChannels --> getSubscriptionAutotranslateDefaultConfig
addUserToDefaultChannels --> getDefaultSubscriptionPref
warn --> compareVersions
warn --> warn
watchUserId --> watch
setAvatarFromServiceWithValidation --> setUserAvatar
setAvatarFromServiceWithValidation --> hasPermissionAsync
getNewUserRoles --> parseCSV
getURLWithoutSettings --> getURL
syncRoomRolePriorityForUserAndRoom --> updateRolePriority
syncRoomRolePriorityForUserAndRoom --> calculateRoomRolePriorityFromRoles
bulkTeamCleanup --> eraseTeamOnRelinquishRoomOwnerships
FileUpload.removeFilesByRoomId --> FileUpload.getStore
FileUpload.removeFilesByRoomId --> FileUploadClass.deleteById
eraseRoomLooseValidation --> deleteRoom
BaseRaw.doNotMixInclusionAndExclusionFields --> BaseRaw.ensureDefaultFields
BaseRaw.updateOne --> BaseRaw.updateOne
BaseRaw.updateOne --> BaseRaw.setUpdatedAt
replace --> replacekey
replace --> translate
replace --> replace
replace --> strLeft
replace --> strRightBack
wrap --> replace
wrap --> replaceEscaped
sendNoWrap --> notifyOnSettingChanged
sendNoWrap --> checkAddressFormat
LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId --> BaseRaw.updateMany
addUserToRoom --> notifyOnRoomChangedById
addUserToRoom --> notifyOnSubscriptionChanged
addUserToRoom --> RoomCoordinatorServer.allowMemberAction
addUserToRoom --> RoomCoordinatorServer.getRoomDirectives
getURL --> getCloudUrl
getURL --> trim
getURL --> ltrim
getURL --> rtrim
updateRolePriority --> calculateRoomRolePriorityFromRoles
calculateRoomRolePriorityFromRoles --> getRoomRolePriorityForRole
eraseTeamOnRelinquishRoomOwnerships --> eraseRoomLooseValidation
FileUploadClass.deleteById --> FileUpload.getStoreByName
deleteRoom --> notifyOnRoomChangedById
deleteRoom --> notifyOnSubscriptionChanged
deleteRoom --> FileUpload.getStore
deleteRoom --> FileUpload.removeFilesByRoomId
BaseRaw.setUpdatedAt --> BaseRaw.setUpdatedAt
replacekey --> replace
strLeft --> makeString
strRightBack --> makeString
replaceEscaped --> replace
BaseRaw.updateMany --> BaseRaw.updateMany
BaseRaw.updateMany --> BaseRaw.setUpdatedAt
getCloudUrl --> ltrim
getCloudUrl --> rtrim
ltrim --> makeString
ltrim --> defaultToWhiteSpace
rtrim --> makeString
rtrim --> defaultToWhiteSpace
Fix with AI
Fix the following security vulnerability found by Hacktron.
File: apps/meteor/app/api/server/v1/users.ts
Lines: 2118-2130
Severity: high
Vulnerability: Missing authorization check in users.update API allows account takeover and privilege escalation
Description:
The `users.update` API endpoint does not enforce sufficient authorization checks before calling `saveUser` to update user information. While it requires 2FA, it lacks explicit permission requirements (e.g., `edit-other-user-info`), potentially allowing authenticated users to modify the profiles of other users.
Affected Code:
API.v1.post(
'users.update',
{
authRequired: true,
twoFactorRequired: true,
body: isUsersUpdateParamsPOST,
response: {
200: userObjectResponse,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
// ...
await saveUser(this.userId, userData, { auditStore });
// ...
},
);
Fix this vulnerability. Only change what's necessary - don't modify unrelated code.
Triage: Reply !fp <reason> (false positive), !valid (confirmed), or !accepted_risk <reason>. Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing. Any other reply is saved as a triage note.
- users.ts setAvatar: cast service through 'rest' overload literal since
the helper accepts arbitrary strings at runtime but the TS overload
for Buffer dataURI restricts the type.
- UserProvider: restore the local logout Emitter for onLogout(cb)
consumers (e2ee cleanup, etc.) — only the sdk.call('logoutCleanUp')
side effect is gone, the in-process broadcaster stays.
- useOpenRoom: cast Partial<IRoom> response through unknown to IRoom to
match downstream consumers that expect the full shape.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/meteor/client/providers/UserProvider/UserProvider.tsx (1)
32-36: 💤 Low valueDrop the implementation comment.
This explanatory block belongs in the PR description/commit message rather than inline. As per coding guidelines, "Avoid code comments in the implementation".
♻️ Proposed change
-// Local logout broadcaster — `onLogout(cb)` consumers (e.g. e2ee cleanup) still -// subscribe to this. The post-logout side effects that used to require a -// `sdk.call('logoutCleanUp')` round-trip (afterLogoutCleanUpCallback + -// Apps.IPostUserLoggedOut) now fire server-side via `Accounts.onLogout` and -// `POST /v1/users.logout`, so this emitter is purely client-side fan-out. const ee = new Emitter();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/client/providers/UserProvider/UserProvider.tsx` around lines 32 - 36, Remove the explanatory implementation comment block above the local logout broadcaster and associated notes; specifically delete the multi-line comment that references onLogout(cb), afterLogoutCleanUpCallback, Accounts.onLogout, POST /v1/users.logout, and Apps.IPostUserLoggedOut so only the code implementing the local logout emitter remains (no inline explanation).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@apps/meteor/client/providers/UserProvider/UserProvider.tsx`:
- Around line 32-36: Remove the explanatory implementation comment block above
the local logout broadcaster and associated notes; specifically delete the
multi-line comment that references onLogout(cb), afterLogoutCleanUpCallback,
Accounts.onLogout, POST /v1/users.logout, and Apps.IPostUserLoggedOut so only
the code implementing the local logout emitter remains (no inline explanation).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3e657c99-f150-4d26-b826-f87b02e52185
📒 Files selected for processing (3)
apps/meteor/app/api/server/v1/users.tsapps/meteor/client/providers/UserProvider/UserProvider.tsxapps/meteor/client/views/room/hooks/useOpenRoom.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/meteor/client/views/room/hooks/useOpenRoom.ts
- apps/meteor/app/api/server/v1/users.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{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/client/providers/UserProvider/UserProvider.tsx
🧠 Learnings (2)
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.
Applied to files:
apps/meteor/client/providers/UserProvider/UserProvider.tsx
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.
Applied to files:
apps/meteor/client/providers/UserProvider/UserProvider.tsx
🔇 Additional comments (2)
apps/meteor/client/providers/UserProvider/UserProvider.tsx (2)
2-2: LGTM!Also applies to: 22-22
37-38: LGTM!
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/meteor/app/api/server/v1/users.ts">
<violation number="1" location="apps/meteor/app/api/server/v1/users.ts:350">
P1: Unsafe type assertion `as 'rest'` on a value that may not be `'rest'`. If `fields.service` provides a value like an OAuth provider name (e.g., `"github"`), the cast silences the type error without changing runtime behavior: `setUserAvatar` will receive the non-`'rest'` value, bypass the `service === 'rest'` branch, and fall into `RocketChatFile.dataURIParse(dataURI)` with a `Buffer` argument — which expects a string and will fail at runtime.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| await setUserAvatar(user, fileBuffer, mimetype, 'rest'); | ||
| const service = typeof fields.service === 'string' && fields.service.length > 0 ? fields.service : 'rest'; | ||
|
|
||
| await setUserAvatar(user, fileBuffer, mimetype, service as 'rest'); |
There was a problem hiding this comment.
P1: Unsafe type assertion as 'rest' on a value that may not be 'rest'. If fields.service provides a value like an OAuth provider name (e.g., "github"), the cast silences the type error without changing runtime behavior: setUserAvatar will receive the non-'rest' value, bypass the service === 'rest' branch, and fall into RocketChatFile.dataURIParse(dataURI) with a Buffer argument — which expects a string and will fail at runtime.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/users.ts, line 350:
<comment>Unsafe type assertion `as 'rest'` on a value that may not be `'rest'`. If `fields.service` provides a value like an OAuth provider name (e.g., `"github"`), the cast silences the type error without changing runtime behavior: `setUserAvatar` will receive the non-`'rest'` value, bypass the `service === 'rest'` branch, and fall into `RocketChatFile.dataURIParse(dataURI)` with a `Buffer` argument — which expects a string and will fail at runtime.</comment>
<file context>
@@ -347,7 +347,7 @@ API.v1
const service = typeof fields.service === 'string' && fields.service.length > 0 ? fields.service : 'rest';
- await setUserAvatar(user, fileBuffer, mimetype, service);
+ await setUserAvatar(user, fileBuffer, mimetype, service as 'rest');
return API.v1.success();
</file context>
| await setUserAvatar(user, fileBuffer, mimetype, service as 'rest'); | |
| if (service !== 'rest') { | |
| return API.v1.failure("The 'service' param must be 'rest' when uploading a file"); | |
| } | |
| await setUserAvatar(user, fileBuffer, mimetype, service); |
Per review feedback — getRoomByTypeAndName may be replaced by a breaking change (e.g. URLs moving to point directly to rid), so the DDP method stays as-is and the publications/room/index.ts file is restored to its develop baseline. Removed: - GET /v1/rooms.getByTypeAndName endpoint + rest-typings entry - getRoomByTypeAndNameMethod extraction + deprecation log - Client callers (useOpenRoom, EmbeddedPreload) restored to useMethod Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Continues the DDP→REST sweep started in #40659, #40711, #40675, #40724. This batch tackles the next set of client DDP callers whose endpoints didn't exist yet. The DDP methods stay registered for external SDK/mobile clients and each gets a
methodDeprecationLogger.method(NAME, '9.0.0', '/v1/...')line pointing at the new REST route.New endpoints
verifyEmail+afterVerifyEmailPOST /v1/users.verifyEmailcloud:connectWorkspacePOST /v1/cloud.connectWorkspacemanage-cloudpermauthorization:addPermissionToRolePOST /v1/permissions.addRoleauthorization:removeRoleFromPermissionPOST /v1/permissions.removeRoleclearIntegrationHistoryPOST /v1/integrations.history.clearreplayOutgoingIntegrationPOST /v1/integrations.outgoing.replayEndpoint extensions
POST /v1/users.setAvatarnow accepts an optionalservicemultipart field (stored asavatarOrigin), replacingsetAvatarFromServiceDDP method.Server-side hook
afterLogoutCleanUpCallback+Apps.IPostUserLoggedOutmove into aAccounts.onLogouthandler +/v1/users.logoutpost-token-unset block. Clientsdk.call('logoutCleanUp')dropped — both DDP and REST logout paths now fire the callbacks server-side.Reverted from this batch
getRoomByTypeAndName— per review feedback, kept as DDP. It may be replaced by a breaking change (e.g. URL routing moving directly to rid) so the migration is deferred.Known follow-ups
resetPassword(web-ui-registration) +checkRegistrationSecretURL+ anonymousregisterUserflow remain.Test plan
avatarOriginmatches the service nameafterLogoutCleanUpCallbackserver logs firePOST /v1/users.logout→ same callbacks fire🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements
Deprecations