-
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?
Changes from all commits
49051b1
3ca9f16
0b0a9a2
7bbe051
d02ff34
3d6bfed
83f3da5
4aea9c5
11eaf8c
668c2a3
fe4ed1f
ba3990c
3c5c1e0
ecf59a7
e76d829
4ed51c6
b94212c
7586e19
9c2b6db
5cf8c84
eac64ef
b65be50
9ce09a4
f86de12
73403e2
2a47751
64cf87c
6d3d051
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,11 @@ | ||
| import type { IncomingHttpHeaders } from 'http'; | ||
|
|
||
| import { hashLoginToken } from '@rocket.chat/account-utils'; | ||
| import { InstanceStatus } from '@rocket.chat/instance-status'; | ||
| import { getHeader } from '@rocket.chat/tools'; | ||
| import { Accounts } from 'meteor/accounts-base'; | ||
| import { Meteor } from 'meteor/meteor'; | ||
|
|
||
| import type { ILoginAttempt } from '../../app/authentication/server/ILoginAttempt'; | ||
| import { getClientAddress } from '../lib/getClientAddress'; | ||
| import { deviceManagementEvents } from '../services/device-management/events'; | ||
| import { sauEvents } from '../services/sauMonitor/events'; | ||
|
|
||
|
|
@@ -19,45 +20,40 @@ Accounts.onLogin((info: ILoginAttempt) => { | |
| } | ||
|
|
||
| const { resume } = methodArguments.find((arg) => 'resume' in arg) ?? {}; | ||
| const loginToken = resume ? hashLoginToken(resume) : ''; | ||
| const instanceId = InstanceStatus.id(); | ||
| const clientAddress = getClientAddress(info.connection); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getClientAddress internally calls for It should probably use the same
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| const userAgent = getHeader(httpHeaders, 'user-agent'); | ||
| const host = getHeader(httpHeaders, 'host'); | ||
|
|
||
| const eventObject = { | ||
| sauEvents.emit('sau.accounts.login', { | ||
| userId: info.user._id, | ||
| connection: { | ||
| ...info.connection, | ||
| ...(resume && { loginToken: Accounts._hashLoginToken(resume) }), | ||
| instanceId: InstanceStatus.id(), | ||
| httpHeaders: httpHeaders as IncomingHttpHeaders, | ||
| }, | ||
| }; | ||
| sauEvents.emit('accounts.login', eventObject); | ||
| deviceManagementEvents.emit('device-login', eventObject); | ||
| instanceId, | ||
| userAgent, | ||
| loginToken, | ||
| connectionId: info.connection.id, | ||
| clientAddress, | ||
| host, | ||
| }); | ||
|
|
||
| deviceManagementEvents.emit('device-login', { userId: info.user._id, userAgent, loginToken, clientAddress }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Line 38 emits Additionally, the payloads differ: sauMonitorHooks includes 🤖 Prompt for AI Agents |
||
| }); | ||
|
|
||
| Accounts.onLogout((info) => { | ||
| const { httpHeaders } = info.connection; | ||
|
|
||
| if (!info.user) { | ||
| return; | ||
| } | ||
| sauEvents.emit('accounts.logout', { | ||
| userId: info.user._id, | ||
| connection: { instanceId: InstanceStatus.id(), ...info.connection, httpHeaders: httpHeaders as IncomingHttpHeaders }, | ||
| }); | ||
|
|
||
| sauEvents.emit('sau.accounts.logout', { userId: info.user._id, sessionId: info.connection.id }); | ||
| }); | ||
|
|
||
| Meteor.onConnection((connection) => { | ||
| connection.onClose(async () => { | ||
| const { httpHeaders } = connection; | ||
| sauEvents.emit('socket.disconnected', { | ||
| instanceId: InstanceStatus.id(), | ||
| ...connection, | ||
| httpHeaders: httpHeaders as IncomingHttpHeaders, | ||
| }); | ||
| sauEvents.emit('sau.socket.disconnected', { connectionId: connection.id, instanceId: InstanceStatus.id() }); | ||
| }); | ||
| }); | ||
|
|
||
| Meteor.onConnection((connection) => { | ||
| const { httpHeaders } = connection; | ||
|
|
||
| sauEvents.emit('socket.connected', { instanceId: InstanceStatus.id(), ...connection, httpHeaders: httpHeaders as IncomingHttpHeaders }); | ||
| // 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 }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,5 @@ | ||
| import type { ISocketConnectionLogged } from '@rocket.chat/core-typings'; | ||
| import { Emitter } from '@rocket.chat/emitter'; | ||
|
|
||
| export const deviceManagementEvents = new Emitter<{ | ||
| 'device-login': { userId: string; connection: ISocketConnectionLogged }; | ||
| 'device-login': { userId: string; userAgent: string; loginToken?: string; clientAddress: string }; | ||
| }>(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,23 @@ | ||
| import type { IDeviceManagementService } from '@rocket.chat/core-services'; | ||
| import { ServiceClassInternal } from '@rocket.chat/core-services'; | ||
| import { getHeader } from '@rocket.chat/tools'; | ||
|
|
||
| import { deviceManagementEvents } from './events'; | ||
| import { getClientAddress } from '../../lib/getClientAddress'; | ||
|
|
||
| export class DeviceManagementService extends ServiceClassInternal implements IDeviceManagementService { | ||
| protected name = 'device-management'; | ||
|
|
||
| constructor() { | ||
| super(); | ||
|
|
||
| this.onEvent('accounts.login', async (data) => { | ||
| this.onEvent('accounts.login', async ({ userId, connection }) => { | ||
| // TODO need to add loginToken to data | ||
| deviceManagementEvents.emit('device-login', data); | ||
| deviceManagementEvents.emit('device-login', { | ||
| userId, | ||
| userAgent: getHeader(connection.httpHeaders, 'user-agent'), | ||
| clientAddress: getClientAddress(connection), | ||
| }); | ||
| }); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,16 @@ | ||
| import type { ISocketConnection, ISocketConnectionLogged } from '@rocket.chat/core-typings'; | ||
| import { Emitter } from '@rocket.chat/emitter'; | ||
|
|
||
| export const sauEvents = new Emitter<{ | ||
| 'accounts.login': { userId: string; connection: ISocketConnectionLogged }; | ||
| 'accounts.logout': { userId: string; connection: ISocketConnection }; | ||
| 'socket.connected': ISocketConnection; | ||
| 'socket.disconnected': ISocketConnection; | ||
| 'sau.accounts.login': { | ||
| userId: string; | ||
| instanceId: string; | ||
| connectionId: string; | ||
| loginToken?: string; | ||
| clientAddress: string; | ||
| userAgent: string; | ||
| host: string; | ||
| }; | ||
| 'sau.accounts.logout': { userId: string; sessionId: string }; | ||
| 'sau.socket.connected': { instanceId: string; connectionId: string }; | ||
| 'sau.socket.disconnected': { instanceId: string; connectionId: string }; | ||
| }>(); |
Uh oh!
There was an error while loading. Please reload this page.