Files
upstream-mirror 30810eecd7
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 2s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

179 lines
4.9 KiB
TypeScript

import type { ILivechatAgent, IUser, Serialized } from '@rocket.chat/core-typings';
import { createTransformFromUpdateFilter } from '@rocket.chat/mongo-adapter';
import { create } from 'zustand';
import { sdk } from './SDKClient';
import { Users } from '../stores';
export const useUserDataSyncReady = create(() => false);
type RawUserData = Serialized<
Pick<
IUser,
| '_id'
| 'type'
| 'name'
| 'username'
| 'emails'
| 'status'
| 'statusDefault'
| 'statusText'
| 'statusExpiresAt'
| 'statusConnection'
| 'avatarOrigin'
| 'utcOffset'
| 'language'
| 'settings'
| 'roles'
| 'active'
| 'defaultRoom'
| 'customFields'
| 'oauth'
| 'createdAt'
| '_updatedAt'
| 'avatarETag'
> & { statusLivechat?: ILivechatAgent['statusLivechat'] }
>;
const updateUser = (userData: IUser): void => {
const user = Users.state.get(userData._id);
if (!user?._updatedAt || user._updatedAt.getTime() < userData._updatedAt.getTime()) {
Users.state.store(userData);
return;
}
// delete data already on user's collection as those are newer
for (const key of Object.keys(user)) {
delete userData[key as keyof IUser];
}
Users.state.update(
({ _id }) => _id === user._id,
(user) => ({ ...user, ...userData }),
);
};
let cancel: undefined | (() => void);
export const synchronizeUserData = async (uid: IUser['_id']): Promise<RawUserData | void> => {
if (!uid) return;
// Remove data from any other user that we may have retained
Users.state.remove((record) => record._id !== uid);
cancel?.();
const result = sdk.stream('notify-user', [`${uid}/userData`], (data) => {
switch (data.type) {
case 'inserted': {
const { type, id, ...user } = data;
Users.state.store(user.data);
break;
}
case 'updated': {
const transform = createTransformFromUpdateFilter<IUser>({ $unset: data.unset as Record<string, 1>, $set: data.diff });
Users.state.update(({ _id }) => _id === uid, transform);
break;
}
case 'removed':
Users.state.delete(uid);
break;
}
});
cancel = result.stop;
await result.ready();
const { success: _success, ldap, lastLogin, services: rawServices, ...fromMe } = await sdk.rest.get('/v1/me');
if (fromMe._id) {
const existingUser = Users.state.get(uid);
const { email: _meEmail, ...meFields } = fromMe;
const { email, cloud, resume, email2fa, emailCode, ...services } = (rawServices ?? {}) as NonNullable<IUser['services']>;
const mergedEmail2fa =
email2fa &&
(() => {
const { changedAt: apiChangedAt, ...email2faRest } = email2fa;
let changedAt: Date | undefined;
if (apiChangedAt != null) {
const parsed = new Date(apiChangedAt as string | number | Date);
if (!Number.isNaN(parsed.getTime())) {
changedAt = parsed;
}
}
if (changedAt == null && existingUser?.services?.email2fa?.changedAt != null) {
const prev = existingUser.services.email2fa.changedAt;
changedAt = prev instanceof Date ? prev : new Date(prev);
}
return {
...email2faRest,
...(changedAt != null ? { changedAt } : {}),
};
})();
updateUser({
type: existingUser?.type ?? 'user',
active: existingUser?.active ?? true,
roles: existingUser?.roles ?? [],
...existingUser,
...meFields,
...(rawServices && {
services: {
...(services ? { ...services } : {}),
...(resume
? {
resume: {
...(resume.loginTokens && {
loginTokens: resume.loginTokens.map((token) => ({
...token,
when: new Date('when' in token && token.when ? token.when : 0),
createdAt: 'createdAt' in token && token.createdAt ? new Date(token.createdAt) : new Date(0),
twoFactorAuthorizedUntil: token.twoFactorAuthorizedUntil ? new Date(token.twoFactorAuthorizedUntil) : undefined,
})),
}),
},
}
: {}),
...(cloud
? {
cloud: {
...cloud,
expiresAt: new Date(cloud.expiresAt),
},
}
: {}),
...(emailCode ? { ...emailCode, expire: new Date(emailCode.expire) } : {}),
...(mergedEmail2fa ? { email2fa: mergedEmail2fa } : {}),
...(email?.verificationTokens && {
email: {
verificationTokens: email.verificationTokens.map((token) => ({
...token,
when: new Date(token.when),
})),
},
}),
},
}),
...(lastLogin && {
lastLogin: new Date(lastLogin),
}),
...(meFields.statusExpiresAt && {
statusExpiresAt: new Date(meFields.statusExpiresAt),
}),
ldap: Boolean(ldap),
createdAt: meFields.createdAt != null ? new Date(meFields.createdAt) : (existingUser?.createdAt ?? new Date()),
_updatedAt: new Date(meFields._updatedAt ?? existingUser?._updatedAt ?? Date.now()),
} as IUser);
}
useUserDataSyncReady.setState(true);
return fromMe as unknown as RawUserData;
};
export const removeLocalUserData = () => {
Users.state.replaceAll([]);
localStorage.clear();
};