From c4c51cab046113c6a606e3ef1304c66476b0790a Mon Sep 17 00:00:00 2001 From: Davide Bolognesi Date: Sun, 26 Apr 2026 20:27:09 +0200 Subject: [PATCH] Package compatible with Meteor 3 (async/await) --- server/status.js | 91 ++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/server/status.js b/server/status.js index a40090b..3210249 100644 --- a/server/status.js +++ b/server/status.js @@ -27,7 +27,7 @@ const statusEvents = new (EventEmitter)(); - "false" if user is online and idle - null if user is offline */ -statusEvents.on('connectionLogin', (advice) => { +statusEvents.on('connectionLogin', async (advice) => { const update = { $set: { 'status.online': true, @@ -41,9 +41,9 @@ statusEvents.on('connectionLogin', (advice) => { // unless ALL existing connections are idle (including this new one), // the user connection becomes active. - const conns = UserConnections.find({ + const conns = await UserConnections.find({ userId: advice.userId - }).fetch(); + }).fetchAsync(); if (!conns.every(c => c.idle)) { update.$set['status.idle'] = false; update.$unset = { @@ -52,17 +52,17 @@ statusEvents.on('connectionLogin', (advice) => { } // in other case, idle field remains true and no update to lastActivity. - Meteor.users.update(advice.userId, update); + await Meteor.users.updateAsync(advice.userId, update); }); -statusEvents.on('connectionLogout', (advice) => { - const conns = UserConnections.find({ +statusEvents.on('connectionLogout', async (advice) => { + const conns = await UserConnections.find({ userId: advice.userId - }).fetch(); + }).fetchAsync(); if (conns.length === 0) { // Go offline if we are the last connection for this user // This includes removing all idle information - Meteor.users.update(advice.userId, { + await Meteor.users.updateAsync(advice.userId, { $set: { 'status.online': false }, @@ -85,7 +85,7 @@ statusEvents.on('connectionLogout', (advice) => { } // The dropped connection was already idle const latestLastActivity = new Date(Math.max(...conns.map(conn => conn.lastActivity))); - Meteor.users.update(advice.userId, { + await Meteor.users.updateAsync(advice.userId, { $set: { 'status.idle': true, 'status.lastActivity': latestLastActivity @@ -101,10 +101,10 @@ statusEvents.on('connectionLogout', (advice) => { TODO: There is a race condition when switching between tabs, leaving the user inactive while idle goes from one tab to the other. It can probably be smoothed out. */ -statusEvents.on('connectionIdle', (advice) => { - const conns = UserConnections.find({ +statusEvents.on('connectionIdle', async (advice) => { + const conns = await UserConnections.find({ userId: advice.userId - }).fetch(); + }).fetchAsync(); if (!conns.every(c => c.idle)) { return; } @@ -113,7 +113,7 @@ statusEvents.on('connectionIdle', (advice) => { // TODO: the race happens here where everyone was idle when we looked for them but now one of them isn't. const latestLastActivity = new Date(Math.max(...conns.map(conn => conn.lastActivity))); - Meteor.users.update(advice.userId, { + await Meteor.users.updateAsync(advice.userId, { $set: { 'status.idle': true, 'status.lastActivity': latestLastActivity @@ -121,8 +121,8 @@ statusEvents.on('connectionIdle', (advice) => { }); }); -statusEvents.on('connectionActive', (advice) => { - Meteor.users.update(advice.userId, { +statusEvents.on('connectionActive', async (advice) => { + await Meteor.users.updateAsync(advice.userId, { $set: { 'status.idle': false }, @@ -133,11 +133,11 @@ statusEvents.on('connectionActive', (advice) => { }); // Reset online status on startup (users will reconnect) -const onStartup = (selector) => { +const onStartup = async (selector) => { if (selector == null) { selector = Meteor?.settings?.packages?.['mizzao:user-status']?.startupQuerySelector || {}; } - return Meteor.users.update(selector, { + return Meteor.users.updateAsync(selector, { $set: { 'status.online': false }, @@ -154,8 +154,8 @@ const onStartup = (selector) => { Local session modification functions - also used in testing */ -const addSession = (connection) => { - UserConnections.upsert(connection.id, { +const addSession = async (connection) => { + await UserConnections.upsertAsync(connection.id, { $set: { ipAddr: connection.clientAddress, userAgent: connection.httpHeaders['user-agent'] @@ -163,8 +163,8 @@ const addSession = (connection) => { }); }; -const loginSession = (connection, date, userId) => { - UserConnections.upsert(connection.id, { +const loginSession = async (connection, date, userId) => { + await UserConnections.upsertAsync(connection.id, { $set: { userId, loginTime: date @@ -181,19 +181,19 @@ const loginSession = (connection, date, userId) => { }; // Possibly trigger a logout event if this connection was previously associated with a user ID -const tryLogoutSession = (connection, date) => { - let conn; - if ((conn = UserConnections.findOne({ +const tryLogoutSession = async (connection, date) => { + const conn = await UserConnections.findOneAsync({ _id: connection.id, userId: { $exists: true } - })) == null) { + }); + if (conn == null) { return false; } // Yes, this is actually a user logging out. - UserConnections.upsert(connection.id, { + await UserConnections.upsertAsync(connection.id, { $unset: { userId: null, loginTime: null @@ -208,13 +208,13 @@ const tryLogoutSession = (connection, date) => { }); }; -const removeSession = (connection, date) => { - tryLogoutSession(connection, date); - UserConnections.remove(connection.id); +const removeSession = async (connection, date) => { + await tryLogoutSession(connection, date); + await UserConnections.removeAsync(connection.id); }; -const idleSession = (connection, date, userId) => { - UserConnections.update(connection.id, { +const idleSession = async (connection, date, userId) => { + await UserConnections.updateAsync(connection.id, { $set: { idle: true, lastActivity: date @@ -228,8 +228,8 @@ const idleSession = (connection, date, userId) => { }); }; -const activeSession = (connection, date, userId) => { - UserConnections.update(connection.id, { +const activeSession = async (connection, date, userId) => { + await UserConnections.updateAsync(connection.id, { $set: { idle: false }, @@ -252,17 +252,24 @@ Meteor.startup(onStartup); // Opening and closing of DDP connections Meteor.onConnection((connection) => { - addSession(connection); + // fire-and-forget, but log any errors + addSession(connection).catch(err => console.error('user-status addSession error:', err)); - return connection.onClose(() => removeSession(connection, new Date())); + connection.onClose(() => { + removeSession(connection, new Date()).catch(err => console.error('user-status removeSession error:', err)); + }); }); // Authentication of a DDP connection -Accounts.onLogin(info => loginSession(info.connection, new Date(), info.user._id)); +Accounts.onLogin((info) => { + loginSession(info.connection, new Date(), info.user._id).catch(err => + console.error('user-status loginSession error:', err) + ); +}); // pub/sub trick as referenced in http://stackoverflow.com/q/10257958/586086 // We used this in the past, but still need this to detect logouts on the same connection. -Meteor.publish(null, function () { +Meteor.publish(null, async function () { // Return null explicitly if this._session is not available, i.e.: // https://github.com/arunoda/meteor-fast-render/issues/41 if (this._session == null) { @@ -271,7 +278,7 @@ Meteor.publish(null, function () { // We're interested in logout events - re-publishes for which a past connection exists if (this.userId == null) { - tryLogoutSession(this._session.connectionHandle, new Date()); + await tryLogoutSession(this._session.connectionHandle, new Date()); } return []; @@ -281,21 +288,21 @@ Meteor.publish(null, function () { // value, however we should never trust it for something security dependent. // If timestamp is not provided (probably due to a desync), use server time. Meteor.methods({ - 'user-status-idle'(timestamp) { + async 'user-status-idle'(timestamp) { check(timestamp, Match.OneOf(null, undefined, Date, Number)); const date = (timestamp != null) ? new Date(timestamp) : new Date(); - idleSession(this.connection, date, this.userId); + await idleSession(this.connection, date, this.userId); }, - 'user-status-active'(timestamp) { + async 'user-status-active'(timestamp) { check(timestamp, Match.OneOf(null, undefined, Date, Number)); // We only use timestamp because it's when we saw activity *on the client* // as opposed to just being notified it. It is probably more accurate even if // a dozen ms off due to the latency of sending it to the server. const date = (timestamp != null) ? new Date(timestamp) : new Date(); - activeSession(this.connection, date, this.userId); + await activeSession(this.connection, date, this.userId); } });