snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
import type { UserStatus } from '@rocket.chat/core-typings';
import 'highlight.js/styles/github.css';
import { sdk } from '../lib/SDKClient';
import { onLoggedIn } from '../lib/loggedIn';
import { clearStoredCredentials, ensureConnectedAndAuthenticated, getDdpSdk, isAuthError } from '../lib/sdk/ddpSdk';
import { isSdkTransportEnabled } from '../lib/sdk/sdkTransportEnabled';
import { STORAGE_KEYS, getStoredItem } from '../lib/sdk/storage';
import { userIdStore } from '../lib/user';
import { removeLocalUserData, synchronizeUserData } from '../lib/userData';
import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent';
import { Users } from '../stores';
const sdkTransportEnabled = isSdkTransportEnabled();
let status: UserStatus | undefined = undefined;
const emitStatusChange = (next: UserStatus | undefined) => {
if (next === status) return;
status = next;
fireGlobalEvent('status-changed', status);
};
if (!sdkTransportEnabled) {
// Flag off: develop's exact onLoggedIn handler — single Accounts.onLogin
// hook, no SDK auth gate, no syncOnce dedup, no userIdStore.subscribe
// belt-and-braces.
onLoggedIn(async () => {
const uid = userIdStore.getState();
if (!uid) return;
const user = await synchronizeUserData(uid);
if (!user) return;
const utcOffset = -new Date().getTimezoneOffset() / 60;
if (user.utcOffset !== utcOffset) {
void sdk.rest.post('/v1/users.setPreferences', { data: { utcOffset } });
}
emitStatusChange(user.status);
});
} else {
const runUserDataSync = async (uid: string) => {
// synchronizeUserData opens a `stream-notify-user/${uid}/userData` sub
// over DDPSDK. The server rejects that sub with "not-allowed" until
// DDPSDK has completed loginWithToken on its own socket. Both
// runUserDataSync and ensureConnectedAndAuthenticated are subscribers
// of userIdStore, so without sequencing the sub races the auth and
// hits the rejection on every re-login. Await the SDK auth here so
// the sub fires authenticated.
const tokenBeforeSync = getStoredItem(STORAGE_KEYS.LOGIN_TOKEN);
try {
await ensureConnectedAndAuthenticated();
} catch {
// non-fatal: sdk.stream queues until DDPSDK eventually auths
}
let user: Awaited<ReturnType<typeof synchronizeUserData>>;
try {
user = await synchronizeUserData(uid);
} catch (error) {
// When the stored token is expired/revoked server-side, the userData
// stream sub + /v1/me come back 401/403, so synchronizeUserData throws
// and useUserDataSyncReady never flips true. useMainReady then stays
// false forever (uid is still truthy from the stale credentials), so
// Preload sits on PageLoading — and because Preload WRAPS
// AuthenticationCheck, the user never reaches LoginPage and the
// workspace looks stuck "loading"/grayed-out. Escalate the dead-session
// signal to a credential wipe so userId drops to null and the router
// falls through to the login screen. Token-stable guard: only clear
// when localStorage still holds the token we synced with, so a parallel
// re-auth that already rotated the token isn't kicked out.
if (isAuthError(error) && getStoredItem(STORAGE_KEYS.LOGIN_TOKEN) === tokenBeforeSync) {
clearStoredCredentials();
}
throw error;
}
if (!user) return;
const utcOffset = -new Date().getTimezoneOffset() / 60;
if (user.utcOffset !== utcOffset) {
void sdk.rest.post('/v1/users.setPreferences', { data: { utcOffset } });
}
emitStatusChange(user.status);
};
// Both `onLoggedIn` (from accounts-base) and `userIdStore.subscribe`
// (belt-and-braces in case the loggedInAndDataReadyCallback's user-await
// autorun gets wedged on logout → fresh login) fire for the same uid on a
// successful login. runUserDataSync calls userSetUtcOffset which is
// rate-limited on CI/prod, so without a shared guard the second call
// returns 400 too-many-requests and downstream REST calls (sessions/list
// etc.) start coming back 401 because the rate limiter throttles auth
// checks for the rest of the window. Use a single guarded sync gate, but
// reset it on failure so SAML/oauth/post-logout flows can retry — those
// flows depend on a second runUserDataSync after the SDK socket finishes
// authenticating, otherwise the userData stream subscription comes back
// nosub and synchronizeUserData throws, leaving useUserDataSyncReady
// false and the page stuck on PageLoading.
let lastSyncedUid: string | undefined;
const syncOnce = (uid: string | undefined): void => {
// Reset on logout transitions so a subsequent re-login (same uid or different)
// runs a fresh sync. Force-logout via the SDK loginWithToken wrap clears
// creds via Accounts._unstoreLoginToken() + setUserId(null), which does NOT
// fire Accounts.onLogout — so without this branch, lastSyncedUid stays set,
// the next login is deduped, runUserDataSync is skipped, and
// useUserDataSyncReady stays false (page wedged on PageLoading).
if (!uid) {
lastSyncedUid = undefined;
return;
}
if (uid === lastSyncedUid) return;
lastSyncedUid = uid;
void runUserDataSync(uid).catch((err) => {
console.warn('[startup] runUserDataSync failed; clearing dedup to allow a retry', err);
if (lastSyncedUid === uid) lastSyncedUid = undefined;
});
};
onLoggedIn(() => {
syncOnce(userIdStore.getState());
});
userIdStore.subscribe((uid) => {
syncOnce(uid);
});
syncOnce(userIdStore.getState());
getDdpSdk().account.onLogout(() => {
lastSyncedUid = undefined;
});
}
Users.use.subscribe(() => {
const uid = userIdStore.getState();
if (!uid) return;
const user = Users.use.getState().get(uid);
if (!user) return;
emitStatusChange(user.status);
});
getDdpSdk().account.onLogout(() => {
removeLocalUserData();
status = undefined;
});
// Session-resume failure (expired stored token on page load): Meteor has already
// cleared Meteor.loginToken before this module runs, userId stays null, and no
// Accounts.onLogout callback fires for this scenario. Detect via the combination
// of missing token and missing uid at module init and clean up residual keys
// (e.g. E2EE public_key / private_key). Do NOT subscribe to userIdStore for this —
// the valid-session resume path is async and would clobber a valid token mid-flight.
if (!userIdStore.getState() && localStorage.getItem('Meteor.loginToken') === null) {
removeLocalUserData();
}