-
Notifications
You must be signed in to change notification settings - Fork 13k
chore: improve the way we send DDP connection data to hooks #38268 #38294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
|
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 |
|
WalkthroughServer event architecture refactored to replace direct socket connection object passing with explicit, typed payloads. Connection metadata like userAgent, loginToken, clientAddress are now individually passed in event payloads, reducing dependency on ISocketConnection types. New getHeader utility added for safe HTTP header extraction. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)
135-142: Update logout log messages to the new event name.The warning strings still mention
'accounts.logout', which no longer matches the hook name and can mislead debugging.🔧 Suggested fix
- if (!userId) { - logger.warn({ msg: "Received 'accounts.logout' event without 'userId'" }); + if (!userId) { + logger.warn({ msg: "Received 'sau.accounts.logout' event without 'userId'" }); return; } if (!sessionId) { - logger.warn({ msg: "Received 'accounts.logout' event without 'sessionId'" }); + logger.warn({ msg: "Received 'sau.accounts.logout' event without 'sessionId'" }); return; }
🤖 Fix all issues with AI agents
In `@packages/core-typings/src/sau/SocketConnectedPayload.ts`:
- Around line 1-3: Remove the inline comment inside the SocketConnectedPayload
type and replace the placeholder with an explicit empty payload type;
specifically, update export type SocketConnectedPayload to an explicit empty
shape (e.g., use Record<string, never> or an empty object type) so the intent is
encoded in the type rather than a comment and the inline comment is removed.
🧹 Nitpick comments (3)
apps/meteor/server/services/sauMonitor/service.ts (1)
35-38: Remove commented-out debug statement.Per coding guidelines, avoid code comments in the implementation. This commented-out console.log should be removed.
Suggested fix
this.onEvent('socket.connected', async (data) => { - // console.log('socket.connected', data); sauEvents.emit('sau.socket.connected', data); });apps/meteor/server/services/device-management/service.ts (1)
14-19: Address TODO: Add loginToken to payload.The TODO indicates
loginTokenshould be added to the device-login payload. TheDeviceLoginPayloadtype already supports an optionalloginTokenfield (as seen insession.tsusage at line 42).Would you like me to help implement the
loginTokenextraction similar to how it's done insauMonitorHooks.ts(usingAccounts._hashLoginToken(resume))? Or should I open an issue to track this task?apps/meteor/server/hooks/sauMonitorHooks.ts (1)
80-86: Remove inline comment and clarifySocketConnectedPayloadintent.The comment on line 82 should be removed per coding guidelines. Additionally, spreading the entire
connectionobject intoSocketConnectedPayloadappears to contradict the PR's goal of avoiding passing full connection objects downstream. Consider extracting only the required fields.Suggested fix
If only specific fields are needed, extract them explicitly:
Meteor.onConnection((connection) => { const socketConnectedEventObject: SocketConnectedPayload = { - // Implement SocketConnectedPayload type in case of using the sau.socket.connected hook - ...connection, + connectionId: connection.id, + // Add other required fields as needed }; sauEvents.emit('sau.socket.connected', socketConnectedEventObject); });If the full connection is intentionally needed here, the comment should at minimum be removed.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #38294 +/- ##
========================================
Coverage 70.77% 70.77%
========================================
Files 3159 3160 +1
Lines 109384 109370 -14
Branches 19644 19709 +65
========================================
- Hits 77412 77407 -5
+ Misses 29939 29929 -10
- Partials 2033 2034 +1
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)
235-245: Replace the inline TODO block with an explicit UA result type.The comment block violates the "no implementation comments" guideline. Move the type information into code as a type alias instead.
However, the proposed solution using
ReturnType<typeof UAParserMobile.uaObject>won't work as expected sinceUAParserCustom.jsis untyped JavaScript. Define the union type explicitly instead:♻️ Suggested approach
+ type UAResult = + | UAParser.IResult + | { device?: { type: string }; app?: { name: string; version: string }; browser?: undefined; os?: undefined } + | { device?: { type: string }; os?: Record<string, string>; app?: { name: string; version: string }; browser?: undefined }; + - const result = ((): any => { + const result = ((): UAResult => {The types correspond to the three code branches: standard UAParser, mobile app, and desktop app respectively.
♻️ Duplicate comments (1)
packages/core-typings/src/sau/SocketConnectedPayload.ts (1)
1-2: Remove inline comment; encode empty payload in the type.Line 1 violates the “no implementation comments” guideline; encode the empty payload via the type instead. As per coding guidelines, avoid code comments in implementation.
♻️ Proposed change
-export type SocketConnectedPayload = {}; +export type SocketConnectedPayload = Record<string, never>;Is `Record<string, never>` the recommended TypeScript pattern for expressing an empty object payload?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues found across 14 files
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)
130-157: Potential logout miss whenloginTokenis absent.
LoginSessionPayload.loginTokenandISession.loginTokenare optional, but this path treats a missing token as “session not found” and returns. That can leave sessions open and skew SAU. Consider enforcing a non-optional token in the payload or adding a fallback to close bysessionIdwhen the token is missing.🔧 Suggested fallback when `loginToken` is missing
- const session = await Sessions.getLoggedInByUserIdAndSessionId<Pick<ISession, 'loginToken'>>(userId, sessionId, { - projection: { loginToken: 1 }, - }); - if (!session?.loginToken) { + const session = await Sessions.getLoggedInByUserIdAndSessionId<Pick<ISession, 'loginToken' | 'instanceId'>>(userId, sessionId, { + projection: { loginToken: 1, instanceId: 1 }, + }); + if (!session) { if (!isProdEnv) { throw new Error('Session not found during logout'); } logger.error({ msg: 'Session not found during logout', userId, sessionId }); return; } + if (!session.loginToken) { + await Sessions.closeByInstanceIdAndSessionId(session.instanceId, sessionId); + return; + } await Sessions.logoutBySessionIdAndUserId({ loginToken: session.loginToken, userId });
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/meteor/server/services/sauMonitor/events.ts`:
- Around line 4-12: The event type for 'sau.accounts.login' is inconsistent with
ISocketConnection because connection.loginToken is optional; update the event
definition in events.ts to make loginToken optional (change loginToken: string
to loginToken?: string) so it matches ISocketConnection and avoids the type
contract violation, then recompile/typecheck; alternatively, if you prefer
forcing a string, update the caller in service.ts where connection.loginToken is
passed to provide a default (e.g., connection.loginToken ?? '')—pick one
approach and apply it consistently across sau.accounts.login, service.ts, and
references in sauMonitorHooks.ts.
🧹 Nitpick comments (3)
apps/meteor/server/services/sauMonitor/service.ts (1)
42-45: Remove commented-out debug code.The commented
console.logstatement should be removed to keep the codebase clean.♻️ Proposed fix
this.onEvent('socket.connected', async (data) => { - // console.log('socket.connected', data); sauEvents.emit('sau.socket.connected', { instanceId: InstanceStatus.id(), connectionId: data.id }); });apps/meteor/server/hooks/sauMonitorHooks.ts (1)
49-58: Consider consolidating the twoMeteor.onConnectionhandlers.Both handlers register on the same connection event. Merging them would reduce overhead and improve code organization.
♻️ Proposed consolidation
Meteor.onConnection((connection) => { + // in case of implementing a listener of this event, define the parameters type in services/sauMonitor/events.ts + sauEvents.emit('sau.socket.connected', { instanceId: InstanceStatus.id(), connectionId: connection.id }); + connection.onClose(async () => { sauEvents.emit('sau.socket.disconnected', { connectionId: connection.id, instanceId: InstanceStatus.id() }); }); }); - -Meteor.onConnection((connection) => { - // in case of implementing a listener of this event, define the parameters type in services/sauMonitor/events.ts - sauEvents.emit('sau.socket.connected', { instanceId: InstanceStatus.id(), connectionId: connection.id }); -});apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)
238-307: Return type includesundefinedbut function always returns a value.The function always returns
{ device: info }(lines 304-306), but the return type is{ device: ISessionDevice } | undefined. This could lead to unnecessary null checks by callers.♻️ Proposed fix
- private _getUserAgentInfo(uaString: string): { device: ISessionDevice } | undefined { + private _getUserAgentInfo(uaString: string): { device: ISessionDevice } {
… into chore/ddp-headers
… into chore/ddp-headers
|
@coderabbitai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues found across 8 files
✅ Actions performedReview triggered.
|
apps/meteor/server/lib/getHeader.ts
Outdated
| @@ -0,0 +1,11 @@ | |||
| export const getHeader = (headers: unknown, key: string): string => { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be in the tools package.
It would also be nice to have unit tests for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could also use some typeguards https://www.typescriptlang.org/docs/handbook/advanced-types.html
| const { resume } = methodArguments.find((arg) => 'resume' in arg) ?? {}; | ||
| const loginToken = resume ? Accounts._hashLoginToken(resume) : ''; | ||
| const instanceId = InstanceStatus.id(); | ||
| const clientAddress = getClientAddress(info.connection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getClientAddress internally calls for headers?.[x-real-ip] which would be undefined if headers is Headers.
It should probably use the same getheader helper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clientAddress is not part of the headers, so getHeader cannot be used in this case.
I kept using getClientAddress as it is already the established way to resolve the client IP and may include internal logic we don't want to bypass.
Co-authored-by: Kevin Aleman <kaleman960@gmail.com>
… into chore/ddp-headers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@apps/meteor/server/hooks/sauMonitorHooks.ts`:
- Line 22: Replace the private Meteor call Accounts._hashLoginToken with the
public hashLoginToken from `@rocket.chat/account-utils`: import { hashLoginToken }
and change the expression that computes loginToken (currently using
Accounts._hashLoginToken(resume)) to use hashLoginToken(resume) (preserving the
ternary behavior with resume). Update any existing imports to add hashLoginToken
and remove reliance on Accounts._hashLoginToken so the code computes loginToken
via the public API.
- Line 38: Remove the duplicate device-login emission in sauMonitorHooks by
deleting the deviceManagementEvents.emit('device-login', { userId:
info.user._id, userAgent, loginToken, clientAddress }) call, and instead update
DeviceManagementService (the service that listens to accounts.login and emits
'device-login') to include the loginToken in its emitted payload so the single
emission covers the session handler in
apps/meteor/ee/server/lib/deviceManagement/session.ts; ensure the emitted object
from DeviceManagementService contains userId, userAgent, clientAddress and
loginToken to match the original payload.
🧹 Nitpick comments (1)
packages/tools/src/getHeader.ts (1)
3-3: Consider exportingHeaderLiketype for consumers.Consumers of
getHeadermight benefit from having access to theHeaderLiketype alias for their own type annotations. This is optional since the function's parameter typing provides guidance.♻️ Optional change
-type HeaderLike = IncomingHttpHeaders | Record<string, string | string[] | undefined>; +export type HeaderLike = IncomingHttpHeaders | Record<string, string | string[] | undefined>;
| } | ||
|
|
||
| const { resume } = methodArguments.find((arg) => 'resume' in arg) ?? {}; | ||
| const loginToken = resume ? Accounts._hashLoginToken(resume) : ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for all uses of Accounts._hashLoginToken in the codebase
rg "Accounts\._hashLoginToken" --type ts --type tsx --type jsRepository: RocketChat/Rocket.Chat
Length of output: 93
🏁 Script executed:
# Also check for any imports or documentation about this API
rg "_hashLoginToken" --type ts --type tsx --type jsRepository: RocketChat/Rocket.Chat
Length of output: 93
🏁 Script executed:
# Look at the full context of the file to understand the usage
cat -n apps/meteor/server/hooks/sauMonitorHooks.tsRepository: RocketChat/Rocket.Chat
Length of output: 2359
🏁 Script executed:
# Search for all uses of _hashLoginToken without file type restrictions
rg "_hashLoginToken"Repository: RocketChat/Rocket.Chat
Length of output: 1684
🏁 Script executed:
# Check if there's any documentation or comments about login tokens in the codebase
rg "hashLoginToken" -C 3Repository: RocketChat/Rocket.Chat
Length of output: 12776
🏁 Script executed:
# Look for Meteor version or package information
cat package.json | head -50Repository: RocketChat/Rocket.Chat
Length of output: 1654
Use public hashLoginToken from @rocket.chat/account-utils instead of private Meteor API.
Accounts._hashLoginToken is a private/internal Meteor API (indicated by underscore prefix). A public equivalent, hashLoginToken, is available in the @rocket.chat/account-utils package and is already used elsewhere in this codebase for the same purpose. Replace line 22 to import and use the public API for consistency and stability.
🤖 Prompt for AI Agents
In `@apps/meteor/server/hooks/sauMonitorHooks.ts` at line 22, Replace the private
Meteor call Accounts._hashLoginToken with the public hashLoginToken from
`@rocket.chat/account-utils`: import { hashLoginToken } and change the expression
that computes loginToken (currently using Accounts._hashLoginToken(resume)) to
use hashLoginToken(resume) (preserving the ternary behavior with resume). Update
any existing imports to add hashLoginToken and remove reliance on
Accounts._hashLoginToken so the code computes loginToken via the public API.
| host, | ||
| }); | ||
|
|
||
| deviceManagementEvents.emit('device-login', { userId: info.user._id, userAgent, loginToken, clientAddress }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all places where device-login is emitted
rg -n "emit\(['\"]device-login['\"]" --type tsRepository: RocketChat/Rocket.Chat
Length of output: 332
🏁 Script executed:
#!/bin/bash
# Look at the sauMonitorHooks.ts file to understand the context
cat -n apps/meteor/server/hooks/sauMonitorHooks.ts | head -60Repository: RocketChat/Rocket.Chat
Length of output: 2359
🏁 Script executed:
#!/bin/bash
# Find and examine the DeviceManagementService
fd "device-management" --type d -x echo {} + | head -5Repository: RocketChat/Rocket.Chat
Length of output: 115
🏁 Script executed:
#!/bin/bash
# Examine the DeviceManagementService to see how it handles device-login events
cat -n apps/meteor/server/services/device-management/service.ts | head -100Repository: RocketChat/Rocket.Chat
Length of output: 1009
🏁 Script executed:
#!/bin/bash
# Check if DeviceManagementService listens to accounts.login
rg -n "accounts.login\|device-login" apps/meteor/server/services/device-management/service.ts -A 3 -B 3Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for where 'accounts.login' event is emitted (not the Accounts hook, but the service event)
rg -n "emit\(['\"]accounts\.login['\"]" --type tsRepository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Check the sauMonitor service to see if it connects to device-management
fd "sauMonitor" --type d apps/meteor/server/services/Repository: RocketChat/Rocket.Chat
Length of output: 105
🏁 Script executed:
#!/bin/bash
# Check if sauMonitor listens to accounts.login or emits it
cat -n apps/meteor/server/services/sauMonitor/service.ts 2>/dev/null || echo "File not found"Repository: RocketChat/Rocket.Chat
Length of output: 1726
🏁 Script executed:
#!/bin/bash
# Search for where the service-level 'accounts.login' event is emitted
rg -n "emit.*accounts\.login" --type ts -B 2 -A 2Repository: RocketChat/Rocket.Chat
Length of output: 857
🏁 Script executed:
#!/bin/bash
# Search for where service events are emitted (broader search)
rg -n "registerMeteorEvents\|emit\(['\"]accounts" --type ts | head -30Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Look for where Accounts.onLogin hook is connected to the service event system
rg -n "registerMeteorEvents\|Accounts\.onLogin" --type ts | head -20Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for event emission that bridges Accounts hooks to service events
fd ".*events.*" apps/meteor/server/services/device-management/ --type fRepository: RocketChat/Rocket.Chat
Length of output: 121
🏁 Script executed:
#!/bin/bash
# Check the device-management events file
cat -n apps/meteor/server/services/device-management/events.tsRepository: RocketChat/Rocket.Chat
Length of output: 307
🏁 Script executed:
#!/bin/bash
# Search for where the service-level 'accounts.login' event is published/emitted
rg -n "publish\(.*accounts\.login\|registerMeteorEvents" --type ts -A 3 -B 3 | head -50Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Look at the app/authentication directory to see how login events are published
fd ".*" apps/meteor/app/authentication/server/ --type f | grep -E "\.(ts|js)$" | head -10Repository: RocketChat/Rocket.Chat
Length of output: 404
🏁 Script executed:
#!/bin/bash
# Search for event publishing related to accounts
rg -n "services\.events\.emit\|publish\(" apps/meteor/server/ --type ts | grep -i login | head -10Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Check the authentication hooks to understand how login events are triggered
cat -n apps/meteor/app/authentication/server/hooks/login.tsRepository: RocketChat/Rocket.Chat
Length of output: 1249
🏁 Script executed:
#!/bin/bash
# Look for where services.events is used to emit accounts.login
rg -n "services\.events\|EventEmitter\|emitAsync" apps/meteor/server/ --type ts | grep -i "account\|login" | head -20Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for where Meteor Accounts.onLogin is bridged to the service event system
rg -n "Accounts\.onLogin\|services.*emit" apps/meteor/server/ --type ts | head -30Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for where 'accounts.login' service event is published from the Meteor hook
rg -n "publishEvent\|emitWithScope\|emit.*accounts" apps/meteor/server/ --type ts | head -40Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Look at the startup directory to see how Meteor hooks are connected to services
fd ".*startup.*" apps/meteor/server/ --type f | head -20Repository: RocketChat/Rocket.Chat
Length of output: 104
🏁 Script executed:
#!/bin/bash
# Check if there's a bridge between Accounts.onLogin and service events
rg -n "Accounts\.on\|services\.events" apps/meteor/server/ --type ts -B 2 -A 2 | head -60Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Check the startup file to understand service initialization
cat -n apps/meteor/server/services/startup.ts | head -100Repository: RocketChat/Rocket.Chat
Length of output: 4296
🏁 Script executed:
#!/bin/bash
# Look at where onLogin hook connects to the service layer
rg -n "Accounts.onLogin" apps/meteor/server/hooks/ --type ts -B 5 -A 10Repository: RocketChat/Rocket.Chat
Length of output: 1318
🏁 Script executed:
#!/bin/bash
# Search for where the 'accounts.login' event (service level) might be emitted
rg -n "emitWithScope\|emit.*accounts\.login" --type ts | head -20Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for where the service-level 'accounts.login' event is emitted
# (the one that DeviceManagementService and SAUMonitorService listen to)
rg -n "\.emit.*accounts\.login\|services\.events\.emit" --type ts | head -40Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Check the MeteorService which may bridge Meteor hooks to service events
cat -n apps/meteor/server/services/meteor/service.tsRepository: RocketChat/Rocket.Chat
Length of output: 11802
🏁 Script executed:
#!/bin/bash
# Search for any bridge between Meteor and service events
fd "meteor" apps/meteor/server/services/ --type f | grep -E "\.ts$"Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for where the 'accounts.login' service-level event is emitted (more comprehensive)
rg -n "accounts\.login" --type ts | grep -E "emit|on" | head -30Repository: RocketChat/Rocket.Chat
Length of output: 1004
🏁 Script executed:
#!/bin/bash
# Search in the entire codebase for who publishes the accounts.login event
rg -n "publishEvent\|api\.events\|api\.broadcast" --type ts | grep -i login | head -20Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Check if there's a connection between Meteor Accounts hooks and service events
rg -n "Accounts\.onLogin" apps/meteor/ --type ts -A 15 | head -100Repository: RocketChat/Rocket.Chat
Length of output: 8267
🏁 Script executed:
#!/bin/bash
# Check the DDP Streamer to see how accounts.login is published
cat -n ee/apps/ddp-streamer/src/DDPStreamer.ts | sed -n '180,190p'Repository: RocketChat/Rocket.Chat
Length of output: 467
🏁 Script executed:
#!/bin/bash
# Check if deviceManagementEvents listeners are registered and what they do
rg -n "deviceManagementEvents\.on\|deviceManagementEvents\.once" --type ts -A 5 | head -50Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Check what listens to deviceManagementEvents to understand impact of duplicates
rg -n "deviceManagementEvents\.on\|deviceManagementEvents\.once" --type ts -A 3Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Confirm all paths where 'accounts.login' can be triggered
rg -n "broadcast.*accounts\.login\|emit.*accounts\.login" --type tsRepository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Search for all listeners/handlers that consume deviceManagementEvents
rg -n "on\(['\"]device-login['\"]" --type ts -A 5Repository: RocketChat/Rocket.Chat
Length of output: 638
🏁 Script executed:
#!/bin/bash
# Verify the flow: check if both emissions happen for the same login
# Look at what triggers Accounts.onLogin vs accounts.login service event
rg -n "DDP_EVENTS.LOGGEDIN\|Accounts.onLogin" --type ts | head -20Repository: RocketChat/Rocket.Chat
Length of output: 48
🏁 Script executed:
#!/bin/bash
# Double-check the flow by examining when both Accounts.onLogin and accounts.login service event fire
# Look at DDPStreamer more thoroughly to understand the full flow
cat -n ee/apps/ddp-streamer/src/DDPStreamer.ts | sed -n '175,195p'Repository: RocketChat/Rocket.Chat
Length of output: 749
🏁 Script executed:
#!/bin/bash
# Check if there are any other places where accounts.login service event is broadcast
rg -n "broadcast\(['\"]accounts\.login" --type tsRepository: RocketChat/Rocket.Chat
Length of output: 175
Remove one device-login emission to prevent duplicates.
Line 38 emits device-login, but DeviceManagementService (line 16 of apps/meteor/server/services/device-management/service.ts) also emits it when listening to the service-level accounts.login event. Both emissions target the same listener in apps/meteor/ee/server/lib/deviceManagement/session.ts, causing the handler to run twice per login.
Additionally, the payloads differ: sauMonitorHooks includes loginToken, while DeviceManagementService omits it (see the TODO comment on line 15 of service.ts). Consolidate the logic into one place, preferably DeviceManagementService, and ensure the loginToken is included.
🤖 Prompt for AI Agents
In `@apps/meteor/server/hooks/sauMonitorHooks.ts` at line 38, Remove the duplicate
device-login emission in sauMonitorHooks by deleting the
deviceManagementEvents.emit('device-login', { userId: info.user._id, userAgent,
loginToken, clientAddress }) call, and instead update DeviceManagementService
(the service that listens to accounts.login and emits 'device-login') to include
the loginToken in its emitted payload so the single emission covers the session
handler in apps/meteor/ee/server/lib/deviceManagement/session.ts; ensure the
emitted object from DeviceManagementService contains userId, userAgent,
clientAddress and loginToken to match the original payload.
Proposed changes (including videos or screenshots)
After some investigation, context gathering, and a discussion with Diego Sampaio, we aligned on the scope of this task.
The goal is to improve how DDP connection headers are handled in sauMonitorHooks.ts.
While the broader issue involves multiple places where the full connection object is passed downstream instead of only the required data, addressing all of those cases is outside the scope of this task.
Changes:
Keeping the object construction at the call site improves traceability of fields (e.g. loginToken, instanceId, httpHeaders) and reduces unnecessary indirection.
Issue(s)
CORE-1529 - Improve DDP header handling
Steps to test or reproduce
Further comments
Please keep in mind that this PR touches sensitive code, such as sessions and login/logout.
Summary by CodeRabbit
Release Notes
Refactor
Tests
✏️ Tip: You can customize this high-level summary in your review settings.