diff --git a/.changeset/blue-ladybugs-raise.md b/.changeset/blue-ladybugs-raise.md new file mode 100644 index 000000000000..44d7a06b4111 --- /dev/null +++ b/.changeset/blue-ladybugs-raise.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Translation files are requested multiple times diff --git a/.changeset/breezy-bugs-jam.md b/.changeset/breezy-bugs-jam.md new file mode 100644 index 000000000000..7e7cc7b8283b --- /dev/null +++ b/.changeset/breezy-bugs-jam.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Managers allowed to make deactivated agent's available diff --git a/.changeset/bright-carpets-fly.md b/.changeset/bright-carpets-fly.md new file mode 100644 index 000000000000..6a8ac2608569 --- /dev/null +++ b/.changeset/bright-carpets-fly.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/core-typings': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +new: ring mobile users on direct conference calls diff --git a/.changeset/bright-snakes-vanish.md b/.changeset/bright-snakes-vanish.md new file mode 100644 index 000000000000..f198bfe93ae9 --- /dev/null +++ b/.changeset/bright-snakes-vanish.md @@ -0,0 +1,9 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue causing `queue time` to be calculated from current time when a room was closed without being served. +Now: +- For served rooms: queue time = servedBy time - queuedAt +- For not served, but open rooms = now - queuedAt +- For not served and closed rooms = closedAt - queuedAt diff --git a/.changeset/brown-clouds-add.md b/.changeset/brown-clouds-add.md new file mode 100644 index 000000000000..6b69289177b5 --- /dev/null +++ b/.changeset/brown-clouds-add.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Performance issue on `Messages.countByType` aggregation caused by unindexed property on messages collection diff --git a/.changeset/bump-patch-1694741499930.md b/.changeset/bump-patch-1694741499930.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1694741499930.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/bump-patch-1694827499043.md b/.changeset/bump-patch-1694827499043.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1694827499043.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/bump-patch-1695163548038.md b/.changeset/bump-patch-1695163548038.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1695163548038.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/bump-patch-1695165575069.md b/.changeset/bump-patch-1695165575069.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1695165575069.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/bump-patch-1695789755733.md b/.changeset/bump-patch-1695789755733.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1695789755733.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/chilled-flies-fold.md b/.changeset/chilled-flies-fold.md new file mode 100644 index 000000000000..17a0f9eb6dc5 --- /dev/null +++ b/.changeset/chilled-flies-fold.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +feat: add sections to room header and user infos menus with menuV2 diff --git a/.changeset/chilled-phones-give.md b/.changeset/chilled-phones-give.md new file mode 100644 index 000000000000..cb0887db0883 --- /dev/null +++ b/.changeset/chilled-phones-give.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-typings": patch +"@rocket.chat/rest-typings": patch +--- + +Fixed `overrideDestinationChannelEnabled` treated as a required param in `integrations.create` and `integration.update` endpoints diff --git a/.changeset/cool-students-tan.md b/.changeset/cool-students-tan.md new file mode 100644 index 000000000000..07760541628a --- /dev/null +++ b/.changeset/cool-students-tan.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +feat(apps): `ActionManagerBusyState` component for apps `ui.interaction` diff --git a/.changeset/cuddly-donkeys-remember.md b/.changeset/cuddly-donkeys-remember.md new file mode 100644 index 000000000000..237aafe7f95b --- /dev/null +++ b/.changeset/cuddly-donkeys-remember.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed custom translations not being displayed diff --git a/.changeset/cuddly-houses-tie.md b/.changeset/cuddly-houses-tie.md new file mode 100644 index 000000000000..76d86a690388 --- /dev/null +++ b/.changeset/cuddly-houses-tie.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Hide Reset TOTP option if 2FA is disabled diff --git a/.changeset/cuddly-ties-bake.md b/.changeset/cuddly-ties-bake.md new file mode 100644 index 000000000000..d912d2969d75 --- /dev/null +++ b/.changeset/cuddly-ties-bake.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/fuselage-ui-kit": minor +"@rocket.chat/uikit-playground": minor +--- + +feat: Add missing variants to UIKit button diff --git a/.changeset/curly-shoes-burn.md b/.changeset/curly-shoes-burn.md new file mode 100644 index 000000000000..67d453ab7245 --- /dev/null +++ b/.changeset/curly-shoes-burn.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Added ability to freeze or completely disable integration scripts through envvars diff --git a/.changeset/custom-emoji-fs.md b/.changeset/custom-emoji-fs.md new file mode 100644 index 000000000000..a9a797f35bc8 --- /dev/null +++ b/.changeset/custom-emoji-fs.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: custom emoji upload with FileSystem method diff --git a/.changeset/dropdown.md b/.changeset/dropdown.md new file mode 100644 index 000000000000..935c12aebe85 --- /dev/null +++ b/.changeset/dropdown.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +New filters to the Rooms Table at `Workspace > Rooms` diff --git a/.changeset/eighty-kids-jog.md b/.changeset/eighty-kids-jog.md new file mode 100644 index 000000000000..6410813d80a6 --- /dev/null +++ b/.changeset/eighty-kids-jog.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/livechat': minor +'@rocket.chat/meteor': minor +--- + +Added new Omnichannel's trigger condition "After starting a chat". diff --git a/.changeset/eleven-icons-tan.md b/.changeset/eleven-icons-tan.md new file mode 100644 index 000000000000..c51124c05dd2 --- /dev/null +++ b/.changeset/eleven-icons-tan.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/core-typings": minor +"@rocket.chat/model-typings": minor +"@rocket.chat/rest-typings": minor +--- + +Introduce the ability to report an user diff --git a/.changeset/empty-ants-enjoy.md b/.changeset/empty-ants-enjoy.md new file mode 100644 index 000000000000..4a55f82d0abf --- /dev/null +++ b/.changeset/empty-ants-enjoy.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +fix: Wrong toast message while creating a new custom sound with an existing name diff --git a/.changeset/fair-cats-destroy.md b/.changeset/fair-cats-destroy.md new file mode 100644 index 000000000000..7dfb74955a94 --- /dev/null +++ b/.changeset/fair-cats-destroy.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +When setting a room as read-only, do not allow previously unmuted users to send messages. diff --git a/.changeset/fast-pumpkins-smoke.md b/.changeset/fast-pumpkins-smoke.md new file mode 100644 index 000000000000..2374776bf3b5 --- /dev/null +++ b/.changeset/fast-pumpkins-smoke.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix: finnish translation diff --git a/.changeset/fast-yaks-collect.md b/.changeset/fast-yaks-collect.md new file mode 100644 index 000000000000..60dd92030163 --- /dev/null +++ b/.changeset/fast-yaks-collect.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fixed an issue where 2fa was not working after an OAuth redirect diff --git a/.changeset/fifty-cars-divide.md b/.changeset/fifty-cars-divide.md new file mode 100644 index 000000000000..6c09cf6869c8 --- /dev/null +++ b/.changeset/fifty-cars-divide.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue with custom OAuth services' settings not being be fully removed diff --git a/.changeset/fluffy-beds-buy.md b/.changeset/fluffy-beds-buy.md new file mode 100644 index 000000000000..f90513b946c3 --- /dev/null +++ b/.changeset/fluffy-beds-buy.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Added support for threaded conversation in Federated rooms. diff --git a/.changeset/fluffy-lions-rage.md b/.changeset/fluffy-lions-rage.md new file mode 100644 index 000000000000..09437a2cb88e --- /dev/null +++ b/.changeset/fluffy-lions-rage.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/livechat": patch +--- + +chore: (Livechat) Replace all `dangerouslySetInnerHTML` with `gazzodown` diff --git a/.changeset/forty-hotels-pretend.md b/.changeset/forty-hotels-pretend.md new file mode 100644 index 000000000000..b23825d5a02a --- /dev/null +++ b/.changeset/forty-hotels-pretend.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Prevent `RoomProvider.useEffect` from subscribing to room-data stream multiple times diff --git a/.changeset/four-parents-cheer.md b/.changeset/four-parents-cheer.md new file mode 100644 index 000000000000..2fbb8e2b279f --- /dev/null +++ b/.changeset/four-parents-cheer.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +load sounds right before playing them diff --git a/.changeset/friendly-glasses-mate.md b/.changeset/friendly-glasses-mate.md new file mode 100644 index 000000000000..6a7a7b4f8546 --- /dev/null +++ b/.changeset/friendly-glasses-mate.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +fix: Time format of Retention Policy diff --git a/.changeset/fuzzy-glasses-divide.md b/.changeset/fuzzy-glasses-divide.md new file mode 100644 index 000000000000..cf77bbde5507 --- /dev/null +++ b/.changeset/fuzzy-glasses-divide.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Added a new Roles bridge to RC Apps-Engine for reading and retrieving role details. diff --git a/.changeset/fuzzy-schools-brake.md b/.changeset/fuzzy-schools-brake.md new file mode 100644 index 000000000000..c6af54a2ef57 --- /dev/null +++ b/.changeset/fuzzy-schools-brake.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix users being created without the `roles` field diff --git a/.changeset/gold-horses-pretend.md b/.changeset/gold-horses-pretend.md new file mode 100644 index 000000000000..a8908b68a23e --- /dev/null +++ b/.changeset/gold-horses-pretend.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed CAS login after popup closes diff --git a/.changeset/gold-moose-press.md b/.changeset/gold-moose-press.md new file mode 100644 index 000000000000..605fb7c649ea --- /dev/null +++ b/.changeset/gold-moose-press.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix moment timestamps language change diff --git a/.changeset/good-elephants-live.md b/.changeset/good-elephants-live.md new file mode 100644 index 000000000000..8cb3e9d87fc4 --- /dev/null +++ b/.changeset/good-elephants-live.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed message fetching method in LivechatBridge for Apps diff --git a/.changeset/green-adults-peel.md b/.changeset/green-adults-peel.md new file mode 100644 index 000000000000..b07f5ea3e6bf --- /dev/null +++ b/.changeset/green-adults-peel.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix pruning messages in a room results in an incorrect message counter diff --git a/.changeset/grumpy-candles-rule.md b/.changeset/grumpy-candles-rule.md new file mode 100644 index 000000000000..28673ce91a73 --- /dev/null +++ b/.changeset/grumpy-candles-rule.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix: rejected conference calls continue to ring diff --git a/.changeset/heavy-baboons-laugh.md b/.changeset/heavy-baboons-laugh.md new file mode 100644 index 000000000000..5c32965dcf62 --- /dev/null +++ b/.changeset/heavy-baboons-laugh.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +User information crashing for some locales diff --git a/.changeset/heavy-cougars-marry.md b/.changeset/heavy-cougars-marry.md new file mode 100644 index 000000000000..893f53352114 --- /dev/null +++ b/.changeset/heavy-cougars-marry.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix performance issue on Engagement Dashboard aggregation diff --git a/.changeset/heavy-zebras-wonder.md b/.changeset/heavy-zebras-wonder.md new file mode 100644 index 000000000000..a1904a81c514 --- /dev/null +++ b/.changeset/heavy-zebras-wonder.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Show correct date for last day time diff --git a/.changeset/hip-hounds-ring.md b/.changeset/hip-hounds-ring.md new file mode 100644 index 000000000000..79dfba6dd031 --- /dev/null +++ b/.changeset/hip-hounds-ring.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Added ability to disable private app installation via envvar (DISABLE_PRIVATE_APP_INSTALLATION) diff --git a/.changeset/hip-mugs-promise.md b/.changeset/hip-mugs-promise.md new file mode 100644 index 000000000000..7100fec026e3 --- /dev/null +++ b/.changeset/hip-mugs-promise.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +improve: System messages for omni-visitor abandonment feature diff --git a/.changeset/honest-glasses-roll.md b/.changeset/honest-glasses-roll.md new file mode 100644 index 000000000000..679f46fb8420 --- /dev/null +++ b/.changeset/honest-glasses-roll.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +chore: Add danger variant to apps action button menus diff --git a/.changeset/honest-mirrors-sit.md b/.changeset/honest-mirrors-sit.md new file mode 100644 index 000000000000..4e4298cb8110 --- /dev/null +++ b/.changeset/honest-mirrors-sit.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Disabled call to tags enterprise endpoint when on community license diff --git a/.changeset/honest-numbers-compete.md b/.changeset/honest-numbers-compete.md new file mode 100644 index 000000000000..1fd017e7fc16 --- /dev/null +++ b/.changeset/honest-numbers-compete.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes SAML full name updates not being mirrored to DM rooms. diff --git a/.changeset/importer-progress-bar.md b/.changeset/importer-progress-bar.md new file mode 100644 index 000000000000..49c04289ddcb --- /dev/null +++ b/.changeset/importer-progress-bar.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed Importer Progress Bar progress indicator + diff --git a/.changeset/kind-students-worry.md b/.changeset/kind-students-worry.md new file mode 100644 index 000000000000..554c1c1204ea --- /dev/null +++ b/.changeset/kind-students-worry.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Make user default role setting public diff --git a/.changeset/lazy-ghosts-design.md b/.changeset/lazy-ghosts-design.md new file mode 100644 index 000000000000..080e9986cebb --- /dev/null +++ b/.changeset/lazy-ghosts-design.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed misleading of 'total' in team members list inside Channel diff --git a/.changeset/loud-sheep-try.md b/.changeset/loud-sheep-try.md new file mode 100644 index 000000000000..f82d0d069554 --- /dev/null +++ b/.changeset/loud-sheep-try.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/fuselage-ui-kit": minor +"@rocket.chat/uikit-playground": minor +--- + +feat: Adding new UIKit components: Callout, Checkbox, Radio Button, Time Picker, Toast Bar, Toggle Switch, Tab Navigation diff --git a/.changeset/lovely-snails-drop.md b/.changeset/lovely-snails-drop.md new file mode 100644 index 000000000000..4e28c6a43c20 --- /dev/null +++ b/.changeset/lovely-snails-drop.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +Fix spotlight search does not find rooms with special or non-latin characters diff --git a/.changeset/lucky-balloons-divide.md b/.changeset/lucky-balloons-divide.md new file mode 100644 index 000000000000..beb4cbfe3b57 --- /dev/null +++ b/.changeset/lucky-balloons-divide.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix engagement dashboard not showing data diff --git a/.changeset/lucky-hounds-sing.md b/.changeset/lucky-hounds-sing.md new file mode 100644 index 000000000000..20b09afaf545 --- /dev/null +++ b/.changeset/lucky-hounds-sing.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fixed wrong user status displayed during mentioning a user in a channel diff --git a/.changeset/many-icons-provide.md b/.changeset/many-icons-provide.md new file mode 100644 index 000000000000..bf82407980ad --- /dev/null +++ b/.changeset/many-icons-provide.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Don't allow to report self messages diff --git a/.changeset/mighty-walls-smash.md b/.changeset/mighty-walls-smash.md new file mode 100644 index 000000000000..54b2846901de --- /dev/null +++ b/.changeset/mighty-walls-smash.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed scrollbar over content in Federated Room List diff --git a/.changeset/moody-comics-cheat.md b/.changeset/moody-comics-cheat.md new file mode 100644 index 000000000000..b8b372306d0e --- /dev/null +++ b/.changeset/moody-comics-cheat.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/release-action': minor +--- + +Add back "Engine Versions" to the release notes diff --git a/.changeset/moody-pans-act.md b/.changeset/moody-pans-act.md new file mode 100644 index 000000000000..6c307604eaa9 --- /dev/null +++ b/.changeset/moody-pans-act.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix seat counter including bots users diff --git a/.changeset/nine-bottles-press.md b/.changeset/nine-bottles-press.md new file mode 100644 index 000000000000..f9a57fa676ad --- /dev/null +++ b/.changeset/nine-bottles-press.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +feat: Add flag to disable teams mention via troubleshoot page diff --git a/.changeset/nine-carrots-listen.md b/.changeset/nine-carrots-listen.md new file mode 100644 index 000000000000..bf5dc72e6cc0 --- /dev/null +++ b/.changeset/nine-carrots-listen.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed layout changing from embedded view when navigating diff --git a/.changeset/odd-elephants-promise.md b/.changeset/odd-elephants-promise.md new file mode 100644 index 000000000000..a12817ed175b --- /dev/null +++ b/.changeset/odd-elephants-promise.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix LinkedIn OAuth broken diff --git a/.changeset/old-federation-card.md b/.changeset/old-federation-card.md new file mode 100644 index 000000000000..fa9879d84426 --- /dev/null +++ b/.changeset/old-federation-card.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Removed old/deprecated Rocket.Chat Federation card from Info page diff --git a/.changeset/perfect-adults-travel.md b/.changeset/perfect-adults-travel.md new file mode 100644 index 000000000000..61ae4ab6dad5 --- /dev/null +++ b/.changeset/perfect-adults-travel.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/fuselage-ui-kit": patch +"@rocket.chat/uikit-playground": patch +--- + +feat(fuselage-ui-kit): Introduce `TabsNavigationBlock` diff --git a/.changeset/pink-zoos-join.md b/.changeset/pink-zoos-join.md new file mode 100644 index 000000000000..dcc1088de0b5 --- /dev/null +++ b/.changeset/pink-zoos-join.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix the code that was setting email URL to an invalid value when SMTP was not set diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 000000000000..aa5cf5401ea3 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,172 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "@rocket.chat/meteor": "6.4.0-develop", + "rocketchat-services": "1.1.4", + "@rocket.chat/account-service": "0.2.4", + "@rocket.chat/authorization-service": "0.2.4", + "@rocket.chat/ddp-streamer": "0.1.4", + "@rocket.chat/omnichannel-transcript": "0.2.4", + "@rocket.chat/presence-service": "0.2.4", + "@rocket.chat/queue-worker": "0.2.4", + "@rocket.chat/stream-hub-service": "0.2.4", + "@rocket.chat/api-client": "0.1.4", + "@rocket.chat/ddp-client": "0.1.4", + "@rocket.chat/omnichannel-services": "0.0.10", + "@rocket.chat/pdf-worker": "0.0.10", + "@rocket.chat/presence": "0.0.10", + "@rocket.chat/ui-theming": "0.0.1", + "@rocket.chat/account-utils": "0.0.1", + "@rocket.chat/agenda": "0.0.2", + "@rocket.chat/base64": "1.0.12", + "@rocket.chat/cas-validate": "0.0.1", + "@rocket.chat/core-services": "0.1.4", + "@rocket.chat/core-typings": "6.3.4", + "@rocket.chat/cron": "0.0.6", + "@rocket.chat/eslint-config": "0.5.2", + "@rocket.chat/favicon": "0.0.1", + "@rocket.chat/fuselage-ui-kit": "1.0.4", + "@rocket.chat/gazzodown": "1.0.4", + "@rocket.chat/i18n": "0.0.1", + "@rocket.chat/instance-status": "0.0.10", + "@rocket.chat/livechat": "1.13.4", + "@rocket.chat/log-format": "0.0.1", + "@rocket.chat/logger": "0.0.1", + "@rocket.chat/mock-providers": "0.0.1", + "@rocket.chat/model-typings": "0.0.10", + "@rocket.chat/models": "0.0.10", + "@rocket.chat/poplib": "0.0.1", + "@rocket.chat/random": "1.2.1", + "@rocket.chat/release-action": "2.1.0", + "@rocket.chat/rest-typings": "6.3.4", + "@rocket.chat/server-fetch": "0.0.1", + "@rocket.chat/sha256": "1.0.9", + "@rocket.chat/tools": "0.0.1", + "@rocket.chat/ui-client": "1.0.4", + "@rocket.chat/ui-composer": "0.0.1", + "@rocket.chat/ui-contexts": "1.0.4", + "@rocket.chat/ui-video-conf": "1.0.4", + "@rocket.chat/uikit-playground": "0.1.4", + "@rocket.chat/web-ui-registration": "1.0.4" + }, + "changesets": [ + "blue-ladybugs-raise", + "breezy-bugs-jam", + "bright-carpets-fly", + "bright-snakes-vanish", + "brown-clouds-add", + "bump-patch-1694741499930", + "bump-patch-1694827499043", + "bump-patch-1695163548038", + "bump-patch-1695165575069", + "bump-patch-1695789755733", + "chilled-flies-fold", + "chilled-phones-give", + "cool-students-tan", + "cuddly-donkeys-remember", + "cuddly-houses-tie", + "cuddly-ties-bake", + "curly-shoes-burn", + "custom-emoji-fs", + "dropdown", + "eighty-kids-jog", + "eleven-icons-tan", + "empty-ants-enjoy", + "fair-cats-destroy", + "fast-pumpkins-smoke", + "fast-yaks-collect", + "fifty-cars-divide", + "fluffy-beds-buy", + "fluffy-lions-rage", + "forty-hotels-pretend", + "four-parents-cheer", + "friendly-glasses-mate", + "fuzzy-glasses-divide", + "fuzzy-schools-brake", + "gold-horses-pretend", + "gold-moose-press", + "good-elephants-live", + "green-adults-peel", + "grumpy-candles-rule", + "heavy-baboons-laugh", + "heavy-cougars-marry", + "heavy-zebras-wonder", + "hip-hounds-ring", + "hip-mugs-promise", + "honest-glasses-roll", + "honest-mirrors-sit", + "honest-numbers-compete", + "importer-progress-bar", + "kind-students-worry", + "lazy-ghosts-design", + "loud-sheep-try", + "lovely-snails-drop", + "lucky-balloons-divide", + "lucky-hounds-sing", + "many-icons-provide", + "mighty-walls-smash", + "moody-comics-cheat", + "moody-pans-act", + "nine-bottles-press", + "nine-carrots-listen", + "odd-elephants-promise", + "old-federation-card", + "perfect-adults-travel", + "pink-zoos-join", + "pretty-bees-give", + "quick-emus-march", + "quiet-phones-sell", + "rare-sheep-yawn", + "real-pets-visit", + "red-windows-admire", + "red-zebras-clap", + "rotten-turtles-agree", + "serious-garlics-clean", + "serious-geckos-drive", + "serious-shrimps-try", + "seven-jobs-tickle", + "shaggy-beans-poke", + "shiny-garlics-carry", + "shiny-tools-worry", + "short-cobras-tell", + "silly-actors-laugh", + "silver-mugs-unite", + "six-buckets-eat", + "slimy-cheetahs-heal", + "slimy-wasps-double", + "slow-lizards-breathe", + "small-rice-repair", + "smooth-planes-cough", + "soft-yaks-matter", + "sour-cows-refuse", + "sour-parrots-nail", + "stale-roses-knock", + "strong-laws-pump", + "swift-birds-build", + "swift-walls-protect", + "tall-pumpkins-cross", + "tame-pens-occur", + "thirty-pumpkins-fix", + "three-ants-give", + "three-birds-tickle", + "tidy-bears-camp", + "tiny-turkeys-burn", + "tough-candles-heal", + "tricky-years-swim", + "unlucky-turtles-search", + "user-mention", + "violet-frogs-cheer", + "warm-hornets-ring", + "wet-frogs-kiss", + "wet-walls-lie", + "wild-spiders-smell", + "wise-onions-trade", + "wise-walls-tan", + "wise-ways-fetch", + "witty-feet-warn", + "yellow-buttons-agree", + "yellow-schools-tell", + "young-trains-glow" + ] +} diff --git a/.changeset/pretty-bees-give.md b/.changeset/pretty-bees-give.md new file mode 100644 index 000000000000..8891420308c5 --- /dev/null +++ b/.changeset/pretty-bees-give.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/rest-typings": minor +--- + +Add option to select what URL previews should be generated for each message. diff --git a/.changeset/quick-emus-march.md b/.changeset/quick-emus-march.md new file mode 100644 index 000000000000..7a6d7b444654 --- /dev/null +++ b/.changeset/quick-emus-march.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/ddp-client': minor +'@rocket.chat/core-services': minor +'@rocket.chat/meteor': minor +--- + +Add new event to notify users directly about new banners diff --git a/.changeset/quiet-phones-sell.md b/.changeset/quiet-phones-sell.md new file mode 100644 index 000000000000..a6222cba16c9 --- /dev/null +++ b/.changeset/quiet-phones-sell.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fixed an issue where oauth login was not working with some providers diff --git a/.changeset/rare-sheep-yawn.md b/.changeset/rare-sheep-yawn.md new file mode 100644 index 000000000000..86c2d7283223 --- /dev/null +++ b/.changeset/rare-sheep-yawn.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/livechat": patch +--- + +fix: Issue caused by spaces in the `config.url` setting diff --git a/.changeset/real-pets-visit.md b/.changeset/real-pets-visit.md new file mode 100644 index 000000000000..d6531285597c --- /dev/null +++ b/.changeset/real-pets-visit.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-typings": patch +--- + +Fixed `default` field not being returned from the `setDefault` endpoints when setting to false diff --git a/.changeset/red-windows-admire.md b/.changeset/red-windows-admire.md new file mode 100644 index 000000000000..48a82b5902cb --- /dev/null +++ b/.changeset/red-windows-admire.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed an issue where timeout for http requests in Apps-Engine bridges was too short diff --git a/.changeset/red-zebras-clap.md b/.changeset/red-zebras-clap.md new file mode 100644 index 000000000000..cd8f832b1835 --- /dev/null +++ b/.changeset/red-zebras-clap.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix importer filters not working diff --git a/.changeset/rotten-turtles-agree.md b/.changeset/rotten-turtles-agree.md new file mode 100644 index 000000000000..f915aa38f758 --- /dev/null +++ b/.changeset/rotten-turtles-agree.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: stop blinking "Room not found" before dm creation diff --git a/.changeset/serious-garlics-clean.md b/.changeset/serious-garlics-clean.md new file mode 100644 index 000000000000..ccdc3c94dda4 --- /dev/null +++ b/.changeset/serious-garlics-clean.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/core-services': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +New AddUser workflow for Federated Rooms diff --git a/.changeset/serious-geckos-drive.md b/.changeset/serious-geckos-drive.md new file mode 100644 index 000000000000..454337399772 --- /dev/null +++ b/.changeset/serious-geckos-drive.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/core-typings': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/ui-client': minor +'@rocket.chat/meteor': minor +--- + +Added Reports Metrics Dashboard to Omnichannel diff --git a/.changeset/serious-shrimps-try.md b/.changeset/serious-shrimps-try.md new file mode 100644 index 000000000000..114293aa104e --- /dev/null +++ b/.changeset/serious-shrimps-try.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed an issue with the positioning of the message menu diff --git a/.changeset/seven-jobs-tickle.md b/.changeset/seven-jobs-tickle.md new file mode 100644 index 000000000000..870bafbb7d9d --- /dev/null +++ b/.changeset/seven-jobs-tickle.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +fix: agent role being removed upon user deactivation diff --git a/.changeset/shaggy-beans-poke.md b/.changeset/shaggy-beans-poke.md new file mode 100644 index 000000000000..31a480638952 --- /dev/null +++ b/.changeset/shaggy-beans-poke.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fix `mention-here` and `mention-all` permissions not being honored diff --git a/.changeset/shiny-garlics-carry.md b/.changeset/shiny-garlics-carry.md new file mode 100644 index 000000000000..117063d93f6f --- /dev/null +++ b/.changeset/shiny-garlics-carry.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix CORS headers not being set for assets diff --git a/.changeset/shiny-tools-worry.md b/.changeset/shiny-tools-worry.md new file mode 100644 index 000000000000..f024eca38d04 --- /dev/null +++ b/.changeset/shiny-tools-worry.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +feat: remove enforce password fallback dependency diff --git a/.changeset/short-cobras-tell.md b/.changeset/short-cobras-tell.md new file mode 100644 index 000000000000..1c28ce7bad11 --- /dev/null +++ b/.changeset/short-cobras-tell.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Reorganized the message menu diff --git a/.changeset/silly-actors-laugh.md b/.changeset/silly-actors-laugh.md new file mode 100644 index 000000000000..aab23e14e5f1 --- /dev/null +++ b/.changeset/silly-actors-laugh.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed Slackbridge was not handling correctly received events from Slack anymore. Events: (Send, edit, delete, react meassages) diff --git a/.changeset/silver-mugs-unite.md b/.changeset/silver-mugs-unite.md new file mode 100644 index 000000000000..be74b1bef215 --- /dev/null +++ b/.changeset/silver-mugs-unite.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: show requested filters only on requested apps view diff --git a/.changeset/six-buckets-eat.md b/.changeset/six-buckets-eat.md new file mode 100644 index 000000000000..f99bcfb71c30 --- /dev/null +++ b/.changeset/six-buckets-eat.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix users not able to login after block time perdiod has passed diff --git a/.changeset/slimy-cheetahs-heal.md b/.changeset/slimy-cheetahs-heal.md new file mode 100644 index 000000000000..44233bc87766 --- /dev/null +++ b/.changeset/slimy-cheetahs-heal.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed selected departments not being displayed due to pagination diff --git a/.changeset/slimy-wasps-double.md b/.changeset/slimy-wasps-double.md new file mode 100644 index 000000000000..b28de342b274 --- /dev/null +++ b/.changeset/slimy-wasps-double.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/meteor': minor +'@rocket.chat/ui-contexts': minor +--- + +UX improvement for the Moderation Console Context bar for viewing the reported messages. The Report reason is now displayed in the reported messages context bar. +The Moderation Action Modal confirmation description is updated to be more clear and concise. diff --git a/.changeset/slow-lizards-breathe.md b/.changeset/slow-lizards-breathe.md new file mode 100644 index 000000000000..fd773b17f5c8 --- /dev/null +++ b/.changeset/slow-lizards-breathe.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed Apps-Engine event `IPostUserCreated` execution diff --git a/.changeset/small-rice-repair.md b/.changeset/small-rice-repair.md new file mode 100644 index 000000000000..67fdff5ca758 --- /dev/null +++ b/.changeset/small-rice-repair.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix validation in app status call that allowed Enterprise apps to be enabled in invalid environments diff --git a/.changeset/smooth-planes-cough.md b/.changeset/smooth-planes-cough.md new file mode 100644 index 000000000000..9ad4239f0342 --- /dev/null +++ b/.changeset/smooth-planes-cough.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/ui-theming': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +feat: high-contrast theme diff --git a/.changeset/soft-yaks-matter.md b/.changeset/soft-yaks-matter.md new file mode 100644 index 000000000000..c326eb7dca70 --- /dev/null +++ b/.changeset/soft-yaks-matter.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +feat: return all broken password policies at once diff --git a/.changeset/sour-cows-refuse.md b/.changeset/sour-cows-refuse.md new file mode 100644 index 000000000000..d907c063f568 --- /dev/null +++ b/.changeset/sour-cows-refuse.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed inviter not informed when inviting member to room via `/invite` slashcommand diff --git a/.changeset/sour-parrots-nail.md b/.changeset/sour-parrots-nail.md new file mode 100644 index 000000000000..1c1eaa3173a8 --- /dev/null +++ b/.changeset/sour-parrots-nail.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed "teams" icon not being displayed on spotlight sidebar search diff --git a/.changeset/stale-roses-knock.md b/.changeset/stale-roses-knock.md new file mode 100644 index 000000000000..25e93fa8c346 --- /dev/null +++ b/.changeset/stale-roses-knock.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: User timezone not being respected on Current Chat's filter diff --git a/.changeset/strong-laws-pump.md b/.changeset/strong-laws-pump.md new file mode 100644 index 000000000000..a4afefd65316 --- /dev/null +++ b/.changeset/strong-laws-pump.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/model-typings': patch +'@rocket.chat/meteor': patch +--- + +Change SAU aggregation to consider only sessions from few days ago instead of the whole past. + +This is particularly important for large workspaces in case the cron job did not run for some time, in that case the amount of sessions would accumulate and the aggregation would take a long time to run. diff --git a/.changeset/swift-birds-build.md b/.changeset/swift-birds-build.md new file mode 100644 index 000000000000..4af3bddd875b --- /dev/null +++ b/.changeset/swift-birds-build.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed unable to create admin user using ADMIN\_\* environment variables diff --git a/.changeset/swift-walls-protect.md b/.changeset/swift-walls-protect.md new file mode 100644 index 000000000000..6e3057775c32 --- /dev/null +++ b/.changeset/swift-walls-protect.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed failing user data exports diff --git a/.changeset/tall-pumpkins-cross.md b/.changeset/tall-pumpkins-cross.md new file mode 100644 index 000000000000..e6cfd8a309b9 --- /dev/null +++ b/.changeset/tall-pumpkins-cross.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/presence": patch +--- + +Fixed presence broadcast being disabled on server restart diff --git a/.changeset/tame-pens-occur.md b/.changeset/tame-pens-occur.md new file mode 100644 index 000000000000..8cb729531fae --- /dev/null +++ b/.changeset/tame-pens-occur.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/model-typings": minor +--- + +Fixed read receipts not getting deleted after corresponding message is deleted \ No newline at end of file diff --git a/.changeset/thirty-pumpkins-fix.md b/.changeset/thirty-pumpkins-fix.md new file mode 100644 index 000000000000..11b92b064e15 --- /dev/null +++ b/.changeset/thirty-pumpkins-fix.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/core-typings': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/tools': minor +'@rocket.chat/meteor': minor +--- + +Added option to select between two script engine options for the integrations diff --git a/.changeset/three-ants-give.md b/.changeset/three-ants-give.md new file mode 100644 index 000000000000..4d33fad05f39 --- /dev/null +++ b/.changeset/three-ants-give.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/cron": patch +"@rocket.chat/meteor": patch +--- + +Increase cron job check delay to 1 min from 5s. + +This reduces MongoDB requests introduced on 6.3. diff --git a/.changeset/three-birds-tickle.md b/.changeset/three-birds-tickle.md new file mode 100644 index 000000000000..0ce911d9f6fa --- /dev/null +++ b/.changeset/three-birds-tickle.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +chore: Increase cache time from 5s to 10s on `getUnits` helpers. This should reduce the number of DB calls made by this method to fetch the unit limitations for a user. diff --git a/.changeset/tidy-bears-camp.md b/.changeset/tidy-bears-camp.md new file mode 100644 index 000000000000..3c2013f79023 --- /dev/null +++ b/.changeset/tidy-bears-camp.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Introduced upsells for the engagement dashboard and device management admin sidebar items in CE workspaces. Additionally, restructured the admin sidebar items to enhance organization. diff --git a/.changeset/tiny-turkeys-burn.md b/.changeset/tiny-turkeys-burn.md new file mode 100644 index 000000000000..a146bd6a0eae --- /dev/null +++ b/.changeset/tiny-turkeys-burn.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fixed an issue on oauth login that caused missing emails to be detected as changed data diff --git a/.changeset/tough-candles-heal.md b/.changeset/tough-candles-heal.md new file mode 100644 index 000000000000..59ad9c1fb3a1 --- /dev/null +++ b/.changeset/tough-candles-heal.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-typings": patch +"@rocket.chat/model-typings": patch +--- + +Fixes a problem where the calculated time for considering the visitor abandonment was the first message from the visitor and not the visitor's reply to the agent. diff --git a/.changeset/tricky-years-swim.md b/.changeset/tricky-years-swim.md new file mode 100644 index 000000000000..2ab1254525b2 --- /dev/null +++ b/.changeset/tricky-years-swim.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/rest-typings": patch +--- + +Deprecate `livechat:getOverviewData` and `livechat:getAgentOverviewData` methods and create API endpoints `livechat/analytics/overview` and `livechat/analytics/agent-overview` to fetch analytics data diff --git a/.changeset/unlucky-turtles-search.md b/.changeset/unlucky-turtles-search.md new file mode 100644 index 000000000000..fffa51020e30 --- /dev/null +++ b/.changeset/unlucky-turtles-search.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed Accounts profile form name change was not working diff --git a/.changeset/user-mention.md b/.changeset/user-mention.md new file mode 100644 index 000000000000..a896a7c12ee4 --- /dev/null +++ b/.changeset/user-mention.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed user mentioning when prepending the username with `>` diff --git a/.changeset/violet-frogs-cheer.md b/.changeset/violet-frogs-cheer.md new file mode 100644 index 000000000000..db48243c40ed --- /dev/null +++ b/.changeset/violet-frogs-cheer.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/fuselage-ui-kit': patch +--- + +Handle invalid context on `VideoConferenceBlock` component diff --git a/.changeset/warm-hornets-ring.md b/.changeset/warm-hornets-ring.md new file mode 100644 index 000000000000..f81cf1efbe92 --- /dev/null +++ b/.changeset/warm-hornets-ring.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-typings": patch +--- + +Use group filter when set to LDAP sync process diff --git a/.changeset/wet-frogs-kiss.md b/.changeset/wet-frogs-kiss.md new file mode 100644 index 000000000000..24395a78f85d --- /dev/null +++ b/.changeset/wet-frogs-kiss.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Missing padding on Omnichannel contacts Contextualbar loading state diff --git a/.changeset/wet-walls-lie.md b/.changeset/wet-walls-lie.md new file mode 100644 index 000000000000..6b18eb497686 --- /dev/null +++ b/.changeset/wet-walls-lie.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes a problem that allowed users to send empty spaces as comment to bypass the "comment required" setting diff --git a/.changeset/wild-spiders-smell.md b/.changeset/wild-spiders-smell.md new file mode 100644 index 000000000000..9694d6259d3a --- /dev/null +++ b/.changeset/wild-spiders-smell.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue where a mailer error was being sent to customers using offline message's form on Omnichannel instead of the translated one diff --git a/.changeset/wise-onions-trade.md b/.changeset/wise-onions-trade.md new file mode 100644 index 000000000000..cb5c731fb6fb --- /dev/null +++ b/.changeset/wise-onions-trade.md @@ -0,0 +1,11 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/i18n": patch +"@rocket.chat/livechat": patch +"@rocket.chat/mock-providers": patch +"@rocket.chat/ui-client": patch +"@rocket.chat/ui-contexts": patch +"@rocket.chat/web-ui-registration": patch +--- + +Fixed the login page language switcher, now the component has a new look, is reactive and the language selection becomes concrete upon login in. Also changed the default language of the login page to be the browser language. diff --git a/.changeset/wise-walls-tan.md b/.changeset/wise-walls-tan.md new file mode 100644 index 000000000000..f558de82ec4c --- /dev/null +++ b/.changeset/wise-walls-tan.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +fix: missing params on updateOwnBasicInfo endpoint diff --git a/.changeset/wise-ways-fetch.md b/.changeset/wise-ways-fetch.md new file mode 100644 index 000000000000..a81063813c35 --- /dev/null +++ b/.changeset/wise-ways-fetch.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed the unread messages mark not showing diff --git a/.changeset/witty-feet-warn.md b/.changeset/witty-feet-warn.md new file mode 100644 index 000000000000..faaa5d44c134 --- /dev/null +++ b/.changeset/witty-feet-warn.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed the video recorder window not closing after permission is denied. diff --git a/.changeset/yellow-buttons-agree.md b/.changeset/yellow-buttons-agree.md new file mode 100644 index 000000000000..a86d172a4544 --- /dev/null +++ b/.changeset/yellow-buttons-agree.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/ui-client': minor +'@rocket.chat/meteor': minor +--- + +feat: add ChangePassword field to Account/Security diff --git a/.changeset/yellow-schools-tell.md b/.changeset/yellow-schools-tell.md new file mode 100644 index 000000000000..c1040fa0856a --- /dev/null +++ b/.changeset/yellow-schools-tell.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/eslint-config': minor +--- + +Unpublished changes in ESLint config diff --git a/.changeset/young-trains-glow.md b/.changeset/young-trains-glow.md new file mode 100644 index 000000000000..77f50812143f --- /dev/null +++ b/.changeset/young-trains-glow.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Fixed the issue of apps icon uneven alignment in case of missing icons inside message composer toolbar & message toolbar menu. diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml new file mode 100644 index 000000000000..808b8acdcbe3 --- /dev/null +++ b/.github/actions/build-docker/action.yml @@ -0,0 +1,73 @@ +name: 'Meteor Docker' + +inputs: + CR_USER: + required: true + CR_PAT: + required: true + node-version: + required: true + description: 'Node version' + type: string + platform: + required: false + description: 'Platform' + type: string + +runs: + using: composite + + steps: + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ inputs.CR_USER }} + password: ${{ inputs.CR_PAT }} + + - name: Restore build + uses: actions/download-artifact@v3 + with: + name: build + path: /tmp/build + + - name: Unpack build + shell: bash + run: | + cd /tmp/build + tar xzf Rocket.Chat.tar.gz + rm Rocket.Chat.tar.gz + + - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: ${{ inputs.node-version }} + cache-modules: true + install: true + + - run: yarn build + shell: bash + + - name: Build Docker images + shell: bash + run: | + args=(rocketchat) + + if [[ '${{ inputs.platform }}' = 'alpine' ]]; then + args+=($SERVICES_PUBLISH) + fi; + + docker compose -f docker-compose-ci.yml build "${args[@]}" + + - name: Publish Docker images to GitHub Container Registry + shell: bash + run: | + args=(rocketchat) + + if [[ '${{ inputs.platform }}' = 'alpine' ]]; then + args+=($SERVICES_PUBLISH) + fi; + + docker compose -f docker-compose-ci.yml push "${args[@]}" diff --git a/.github/actions/meteor-build/action.yml b/.github/actions/meteor-build/action.yml new file mode 100644 index 000000000000..21fec059c8de --- /dev/null +++ b/.github/actions/meteor-build/action.yml @@ -0,0 +1,129 @@ +name: 'Meteor Build' + +inputs: + coverage: + required: false + description: 'Enable coverage' + type: boolean + reset-meteor: + required: false + description: 'Reset Meteor' + type: boolean + node-version: + required: true + description: 'Node version' + type: string + +runs: + using: composite + + steps: + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 4 + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: ${{ inputs.node-version }} + cache-modules: true + install: true + + # - name: Free disk space + # run: | + # sudo apt clean + # docker rmi $(docker image ls -aq) + # df -h + + - name: Cache vite + uses: actions/cache@v3 + with: + path: ./node_modules/.vite + key: vite-local-cache-${{ runner.OS }}-${{ hashFiles('package.json') }} + restore-keys: | + vite-local-cache-${{ runner.os }}- + + - name: Cache meteor local + uses: actions/cache@v3 + with: + path: ./apps/meteor/.meteor/local + key: meteor-local-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/versions') }} + restore-keys: | + meteor-local-cache-${{ runner.os }}- + + - name: Cache meteor + uses: actions/cache@v3 + with: + path: ~/.meteor + key: meteor-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/release') }} + restore-keys: | + meteor-cache-${{ runner.os }}- + + - name: Install Meteor + shell: bash + run: | + # Restore bin from cache + set +e + METEOR_SYMLINK_TARGET=$(readlink ~/.meteor/meteor) + METEOR_TOOL_DIRECTORY=$(dirname "$METEOR_SYMLINK_TARGET") + set -e + LAUNCHER=$HOME/.meteor/$METEOR_TOOL_DIRECTORY/scripts/admin/launch-meteor + if [ -e $LAUNCHER ] + then + echo "Cached Meteor bin found, restoring it" + sudo cp "$LAUNCHER" "/usr/local/bin/meteor" + else + echo "No cached Meteor bin found." + fi + + # only install meteor if bin isn't found + command -v meteor >/dev/null 2>&1 || curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh + + - name: Versions + shell: bash + run: | + npm --versions + yarn -v + node -v + meteor --version + meteor npm --versions + meteor node -v + git version + + - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + + - name: Translation check + shell: bash + run: yarn turbo run translation-check + + - name: Reset Meteor + shell: bash + if: ${{ inputs.reset-meteor == 'true' }} + working-directory: ./apps/meteor + run: meteor reset + + - name: Build Rocket.Chat From Pull Request + shell: bash + if: startsWith(github.ref, 'refs/pull/') == true + env: + METEOR_PROFILE: 1000 + BABEL_ENV: ${{ inputs.coverage == 'true' && 'coverage' || '' }} + run: yarn build:ci -- --directory /tmp/dist + + - name: Build Rocket.Chat + shell: bash + if: startsWith(github.ref, 'refs/pull/') != true + run: yarn build:ci -- --directory /tmp/dist + + - name: Prepare build + shell: bash + run: | + cd /tmp/dist + tar czf /tmp/Rocket.Chat.tar.gz bundle + + - name: Store build + uses: actions/upload-artifact@v3 + with: + name: build + path: /tmp/Rocket.Chat.tar.gz diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index 47da65a57144..d3a463492cbb 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -19,28 +19,28 @@ runs: using: composite steps: - # - name: Cache Node Modules - # if: inputs.cache-modules - # id: cache-node-modules - # uses: actions/cache@v3 - # with: - # path: | - # node_modules - # **/node_modules - # key: node-modules-${{ hashFiles('yarn.lock') }} + - name: Cache Node Modules + if: inputs.cache-modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: | + node_modules + apps/meteor/node_modules + apps/meteor/ee/server/services/node_modules + key: node-modules-${{ hashFiles('yarn.lock') }} # # Could use this command to list all paths to save: # find . -name 'node_modules' -prune | grep -v "/\.meteor/" | grep -v "/meteor/packages/" - name: Use Node.js ${{ inputs.node-version }} id: node-version - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.7.0 with: node-version: ${{ inputs.node-version }} - cache: ${{ steps.cache-node-modules.outputs.cache-hit != 'true' && 'yarn' || '' }} + cache: 'yarn' - name: yarn install - # if: inputs.install && steps.cache-node-modules.outputs.cache-hit != 'true' - if: inputs.install + if: steps.cache-node-modules.outputs.cache-hit != 'true' shell: bash run: yarn diff --git a/.github/workflows/ci-code-check.yml b/.github/workflows/ci-code-check.yml index 421c517b715a..57cdac047423 100644 --- a/.github/workflows/ci-code-check.yml +++ b/.github/workflows/ci-code-check.yml @@ -44,10 +44,32 @@ jobs: - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - name: Cache TypeCheck + uses: actions/cache@v3 + if: matrix.check == 'ts' + with: + path: ./apps/meteor/tsconfig.typecheck.tsbuildinfo + key: typecheck-cache-${{ runner.OS }}-${{ hashFiles('yarn.lock') }}-${{ github.event.issue.number }} + restore-keys: | + typecheck-cache-${{ runner.OS }}-${{ hashFiles('yarn.lock') }} + typecheck-cache-${{ runner.OS }} + typecheck-cache + - name: TS TypeCheck if: matrix.check == 'ts' run: yarn turbo run typecheck + - name: Cache eslint + uses: actions/cache@v3 + if: matrix.check == 'lint' + with: + path: ./apps/meteor/.eslintcache + key: eslintcache-cache-${{ runner.OS }}-${{ hashFiles('yarn.lock') }}-${{ github.event.issue.number }} + restore-keys: | + eslintcache-cache-${{ runner.OS }}-${{ hashFiles('yarn.lock') }} + eslintcache-cache-${{ runner.OS }} + eslintcache-cache + - name: Lint if: matrix.check == 'lint' run: yarn lint diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 28cbdf0ce4e7..e14857a97a09 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -35,9 +35,6 @@ on: release: required: true type: string - publish-container: - default: false - type: boolean shard: default: '[1]' required: false @@ -60,6 +57,8 @@ on: required: false REPORTER_ROCKETCHAT_API_KEY: required: false + CODECOV_TOKEN: + required: false env: MONGO_URL: mongodb://localhost:27017/rocketchat?replicaSet=rs0&directConnection=true @@ -84,7 +83,7 @@ jobs: steps: - name: Launch MongoDB - uses: supercharge/mongodb-github-action@1.9.0 + uses: supercharge/mongodb-github-action@v1.10.0 with: mongodb-version: ${{ matrix.mongodb-version }} mongodb-replica-set: rs0 @@ -101,6 +100,7 @@ jobs: - uses: dtinth/setup-github-actions-caching-for-turbo@v1 - name: Start httpbin container and wait for it to be ready + if: inputs.type == 'api' run: | docker run -d -p 10000:80 --name httpbin-container kennethreitz/httpbin i=0 @@ -117,68 +117,24 @@ jobs: exit 1 fi - - name: yarn build - run: yarn build - - - name: Restore build - uses: actions/download-artifact@v3 - with: - name: build - path: /tmp/build - - - name: Unpack build - run: | - cd /tmp/build - tar xzf Rocket.Chat.tar.gz - rm Rocket.Chat.tar.gz + - run: yarn build - - name: Start containers + - name: Start containers for CE if: inputs.release == 'ce' env: MONGO_URL: 'mongodb://host.docker.internal:27017/rocketchat?replicaSet=rs0&directConnection=true' run: | - docker compose -f docker-compose-ci.yml up -d --build rocketchat + # when we are testing CE, we only need to start the rocketchat container + docker compose -f docker-compose-ci.yml up -d rocketchat - - name: Start containers + - name: Start containers for EE if: inputs.release == 'ee' env: MONGO_URL: 'mongodb://host.docker.internal:27017/rocketchat?replicaSet=rs0&directConnection=true' ENTERPRISE_LICENSE: ${{ inputs.enterprise-license }} TRANSPORTER: ${{ inputs.transporter }} run: | - docker compose -f docker-compose-ci.yml up -d --build - - - name: Login to GitHub Container Registry - if: inputs.publish-container == true && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ secrets.CR_USER }} - password: ${{ secrets.CR_PAT }} - - - name: Publish Docker images to GitHub Container Registry - if: inputs.publish-container == true && inputs.release == 'ce' && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') - run: | - docker compose -f docker-compose-ci.yml push rocketchat - - if [[ '${{ matrix.mongodb-version }}' = '4.4' ]]; then - IMAGE_NAME_BASE="ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${{ inputs.gh-docker-tag }}" - - echo "Push Docker image: ${IMAGE_NAME_BASE}" - - docker tag ${IMAGE_NAME_BASE}.official $IMAGE_NAME_BASE - docker push $IMAGE_NAME_BASE - fi; - - - name: Publish Docker images (services) to GitHub Container Registry - if: inputs.publish-container == true && inputs.release == 'ee' && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') - run: | - docker compose -f docker-compose-ci.yml push \ - authorization-service \ - account-service \ - ddp-streamer-service \ - presence-service \ - stream-hub-service + docker compose -f docker-compose-ci.yml up -d - name: Cache Playwright binaries if: inputs.type == 'ui' @@ -188,7 +144,7 @@ jobs: path: | ~/.cache/ms-playwright # This is the version of Playwright that we are using, if you are willing to upgrade, you should update this. - key: playwright-1.23.1 + key: playwright-1.37.1 - name: Install Playwright if: inputs.type == 'ui' && steps.cache-playwright.outputs.cache-hit != 'true' @@ -249,13 +205,17 @@ jobs: IS_EE: ${{ inputs.release == 'ee' && 'true' || '' }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} - REPORTER_ROCKETCHAT_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }} + REPORTER_ROCKETCHAT_REPORT: ${{ github.event.pull_request.draft != 'true' && 'true' || '' }} + REPORTER_ROCKETCHAT_RUN: ${{ github.run_number }} REPORTER_ROCKETCHAT_BRANCH: ${{ github.ref }} REPORTER_ROCKETCHAT_DRAFT: ${{ github.event.pull_request.draft }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} QASE_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }} + CI: true working-directory: ./apps/meteor - run: yarn test:e2e --shard=${{ matrix.shard }}/${{ inputs.total-shard }} + run: | + yarn prepare + yarn test:e2e --shard=${{ matrix.shard }}/${{ inputs.total-shard }} - name: Store playwright test trace if: inputs.type == 'ui' && always() @@ -279,6 +239,7 @@ jobs: directory: ./apps/meteor flags: e2e verbose: true + token: ${{ secrets.CODECOV_TOKEN }} - name: Store e2e-ee-coverage if: inputs.type == 'ui' && inputs.release == 'ee' diff --git a/.github/workflows/ci-test-unit.yml b/.github/workflows/ci-test-unit.yml index f294ac2dda7b..b4ef5cb273ad 100644 --- a/.github/workflows/ci-test-unit.yml +++ b/.github/workflows/ci-test-unit.yml @@ -6,6 +6,9 @@ on: node-version: required: true type: string + secrets: + CODECOV_TOKEN: + required: false env: MONGO_URL: mongodb://localhost:27017/rocketchat?replicaSet=rs0&directConnection=true @@ -31,3 +34,9 @@ jobs: - name: Unit Test run: yarn testunit + + - uses: codecov/codecov-action@v3 + with: + flags: unit + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09b561675e94..31c2c42718b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,11 +80,10 @@ jobs: echo "DOCKER_TAG: ${DOCKER_TAG}" echo "gh-docker-tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT - build: - name: 📦 Build + packages-build: + name: 📦 Build Packages needs: [release-versions] runs-on: ubuntu-20.04 - steps: - name: Github Info run: | @@ -110,12 +109,6 @@ jobs: cache-modules: true install: true - # - name: Free disk space - # run: | - # sudo apt clean - # docker rmi $(docker image ls -aq) - # df -h - - name: Cache vite uses: actions/cache@v3 with: @@ -124,84 +117,123 @@ jobs: restore-keys: | vite-local-cache-${{ runner.os }}- - - name: Cache meteor local - uses: actions/cache@v3 - with: - path: ./apps/meteor/.meteor/local - key: meteor-local-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/versions') }} - restore-keys: | - meteor-local-cache-${{ runner.os }}- + - uses: dtinth/setup-github-actions-caching-for-turbo@v1 - - name: Cache meteor - uses: actions/cache@v3 - with: - path: ~/.meteor - key: meteor-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/release') }} - restore-keys: | - meteor-cache-${{ runner.os }}- + - name: Build Rocket.Chat Packages + run: yarn build - - name: Install Meteor + build: + name: 📦 Meteor Build - coverage + needs: [release-versions, packages-build] + runs-on: ubuntu-20.04 + + steps: + - name: Github Info run: | - # Restore bin from cache - set +e - METEOR_SYMLINK_TARGET=$(readlink ~/.meteor/meteor) - METEOR_TOOL_DIRECTORY=$(dirname "$METEOR_SYMLINK_TARGET") - set -e - LAUNCHER=$HOME/.meteor/$METEOR_TOOL_DIRECTORY/scripts/admin/launch-meteor - if [ -e $LAUNCHER ] - then - echo "Cached Meteor bin found, restoring it" - sudo cp "$LAUNCHER" "/usr/local/bin/meteor" - else - echo "No cached Meteor bin found." - fi + echo "GITHUB_ACTION: $GITHUB_ACTION" + echo "GITHUB_ACTOR: $GITHUB_ACTOR" + echo "GITHUB_REF: $GITHUB_REF" + echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF" + echo "GITHUB_BASE_REF: $GITHUB_BASE_REF" + echo "github.event_name: ${{ github.event_name }}" + cat $GITHUB_EVENT_PATH - # only install meteor if bin isn't found - command -v meteor >/dev/null 2>&1 || curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh + - uses: actions/checkout@v3 - - name: Versions + - uses: ./.github/actions/meteor-build + with: + node-version: ${{ needs.release-versions.outputs.node-version }} + coverage: true + + build-prod: + name: 📦 Meteor Build - official + needs: [tests-done, release-versions, packages-build] + if: (github.event_name == 'release' || github.ref == 'refs/heads/develop') + runs-on: ubuntu-20.04 + + steps: + - name: Github Info run: | - npm --versions - yarn -v - node -v - meteor --version - meteor npm --versions - meteor node -v - git version + echo "GITHUB_ACTION: $GITHUB_ACTION" + echo "GITHUB_ACTOR: $GITHUB_ACTOR" + echo "GITHUB_REF: $GITHUB_REF" + echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF" + echo "GITHUB_BASE_REF: $GITHUB_BASE_REF" + echo "github.event_name: ${{ github.event_name }}" + cat $GITHUB_EVENT_PATH - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: actions/checkout@v3 - - name: Translation check - run: yarn turbo run translation-check + - uses: ./.github/actions/meteor-build + with: + node-version: ${{ needs.release-versions.outputs.node-version }} + coverage: false - - name: Reset Meteor - if: startsWith(github.ref, 'refs/tags/') == 'true' || github.ref == 'refs/heads/develop' - working-directory: ./apps/meteor - run: meteor reset + build-gh-docker-coverage: + name: 🚢 Build Docker Images for Testing + needs: [build, release-versions] + if: (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') + runs-on: ubuntu-20.04 - - name: Build Rocket.Chat From Pull Request - if: startsWith(github.ref, 'refs/pull/') == true - env: - METEOR_PROFILE: 1000 - run: yarn build:ci -- --directory /tmp/dist + env: + RC_DOCKERFILE: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-dockerfile-alpine || needs.release-versions.outputs.rc-dockerfile }} + RC_DOCKER_TAG: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-docker-tag-alpine || needs.release-versions.outputs.rc-docker-tag }} + DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }} + LOWERCASE_REPOSITORY: ${{ needs.release-versions.outputs.lowercase-repo }} + SERVICES_PUBLISH: 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service' - - name: Build Rocket.Chat - if: startsWith(github.ref, 'refs/pull/') != true - run: yarn build:ci -- --directory /tmp/dist + strategy: + fail-fast: false + matrix: + platform: ['official', 'alpine'] - - name: Prepare build - run: | - cd /tmp/dist - tar czf /tmp/Rocket.Chat.tar.gz bundle + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/build-docker + with: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + node-version: ${{ needs.release-versions.outputs.node-version }} + platform: ${{ matrix.platform }} - - name: Store build - uses: actions/upload-artifact@v3 + build-gh-docker: + name: 🚢 Build Docker Images for Production + needs: [build-prod, release-versions] + runs-on: ubuntu-20.04 + + env: + RC_DOCKERFILE: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-dockerfile-alpine || needs.release-versions.outputs.rc-dockerfile }} + RC_DOCKER_TAG: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-docker-tag-alpine || needs.release-versions.outputs.rc-docker-tag }} + DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }} + LOWERCASE_REPOSITORY: ${{ needs.release-versions.outputs.lowercase-repo }} + SERVICES_PUBLISH: 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service' + + strategy: + fail-fast: false + matrix: + platform: ['official', 'alpine'] + + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/build-docker with: - name: build - path: /tmp/Rocket.Chat.tar.gz + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + node-version: ${{ needs.release-versions.outputs.node-version }} + platform: ${{ matrix.platform }} + + - name: Rename official Docker tag to GitHub Container Registry + if: matrix.platform == 'official' + run: | + IMAGE_NAME_BASE="ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${DOCKER_TAG}" + + echo "Push Docker image: ${IMAGE_NAME_BASE}" + docker tag ${IMAGE_NAME_BASE}.official $IMAGE_NAME_BASE + docker push $IMAGE_NAME_BASE checks: - needs: [release-versions] + needs: [release-versions, packages-build] name: 🔎 Code Check uses: ./.github/workflows/ci-code-check.yml @@ -210,21 +242,22 @@ jobs: test-unit: name: 🔨 Test Unit - needs: [checks, build, release-versions] + needs: [packages-build, release-versions] uses: ./.github/workflows/ci-test-unit.yml with: node-version: ${{ needs.release-versions.outputs.node-version }} + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} test-api: name: 🔨 Test API (CE) - needs: [checks, build, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: type: api release: ce - publish-container: true node-version: ${{ needs.release-versions.outputs.node-version }} lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} rc-dockerfile: ${{ needs.release-versions.outputs.rc-dockerfile }} @@ -238,7 +271,7 @@ jobs: test-ui: name: 🔨 Test UI (CE) - needs: [checks, build, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -264,7 +297,7 @@ jobs: test-api-ee: name: 🔨 Test API (EE) - needs: [checks, build, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -272,7 +305,6 @@ jobs: release: ee transporter: 'nats://nats:4222' enterprise-license: ${{ needs.release-versions.outputs.enterprise-license }} - publish-container: true mongodb-version: "['4.4']" node-version: ${{ needs.release-versions.outputs.node-version }} lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} @@ -287,7 +319,7 @@ jobs: test-ui-ee: name: 🔨 Test UI (EE) - needs: [checks, build, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -311,6 +343,7 @@ jobs: QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} tests-done: name: ✅ Tests Done @@ -326,7 +359,7 @@ jobs: name: 🚀 Publish build assets runs-on: ubuntu-20.04 if: github.event_name == 'release' || github.ref == 'refs/heads/develop' - needs: [tests-done, release-versions] + needs: [build-gh-docker, release-versions] steps: - uses: Bhacaz/checkout-files@v2 diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml index 287c7d298796..937c4d1b6b52 100644 --- a/.github/workflows/new-release.yml +++ b/.github/workflows/new-release.yml @@ -9,9 +9,9 @@ on: default: next required: true options: - - next - - patch - - cut + - next + - patch + - cut base-ref: description: Base version default: develop @@ -25,29 +25,29 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.inputs.base-ref }} - fetch-depth: 0 - token: ${{ secrets.CI_PAT }} - - - name: Setup NodeJS - uses: ./.github/actions/setup-node - with: - node-version: 14.21.3 - cache-modules: true - install: true - - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 - - - name: Build packages - run: yarn build - - - name: "Start release: ${{ github.event.inputs.name }}" - uses: ./packages/release-action - with: - action: ${{ github.event.inputs.name }} - base-ref: ${{ github.event.inputs.base-ref }} - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.CI_PAT }} + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.base-ref }} + fetch-depth: 0 + token: ${{ secrets.CI_PAT }} + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: 14.21.3 + cache-modules: true + install: true + + - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + + - name: Build packages + run: yarn build + + - name: 'Start release: ${{ github.event.inputs.name }}' + uses: ./packages/release-action + with: + action: ${{ github.event.inputs.name }} + base-ref: ${{ github.event.inputs.base-ref }} + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/.yarn/patches/@storybook-react-docgen-typescript-plugin-npm-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0-b31cc57c40.patch b/.yarn/patches/@storybook-react-docgen-typescript-plugin-npm-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0-b31cc57c40.patch new file mode 100644 index 000000000000..ca069ee350c0 --- /dev/null +++ b/.yarn/patches/@storybook-react-docgen-typescript-plugin-npm-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0-b31cc57c40.patch @@ -0,0 +1,130 @@ +diff --git a/dist/generateDocgenCodeBlock.js b/dist/generateDocgenCodeBlock.js +index 0993ac13e4b2aae6d24cf408d6a585b4ddeb7337..1405896291288eb1322d6c42144afd3b4fbd1abf 100644 +--- a/dist/generateDocgenCodeBlock.js ++++ b/dist/generateDocgenCodeBlock.js +@@ -34,7 +34,7 @@ function insertTsIgnoreBeforeStatement(statement) { + * ``` + */ + function setDisplayName(d) { +- return insertTsIgnoreBeforeStatement(typescript_1.default.createExpressionStatement(typescript_1.default.createBinary(typescript_1.default.createPropertyAccess(typescript_1.default.createIdentifier(d.displayName), typescript_1.default.createIdentifier("displayName")), typescript_1.default.SyntaxKind.EqualsToken, typescript_1.default.createLiteral(d.displayName)))); ++ return insertTsIgnoreBeforeStatement(typescript_1.default.factory.createExpressionStatement(typescript_1.default.factory.createBinaryExpression(typescript_1.default.factory.createPropertyAccessExpression(typescript_1.default.factory.createIdentifier(d.displayName), typescript_1.default.factory.createIdentifier("displayName")), typescript_1.default.SyntaxKind.EqualsToken, typescript_1.default.factory.createStringLiteral(d.displayName)))); + } + /** + * Set a component prop description. +@@ -65,7 +65,7 @@ function createPropDefinition(propName, prop, options) { + * + * @param defaultValue Default prop value or null if not set. + */ +- const setDefaultValue = (defaultValue) => typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral("defaultValue"), ++ const setDefaultValue = (defaultValue) => typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral("defaultValue"), + // Use a more extensive check on defaultValue. Sometimes the parser + // returns an empty object. + defaultValue !== null && +@@ -75,12 +75,19 @@ function createPropDefinition(propName, prop, options) { + (typeof defaultValue.value === "string" || + typeof defaultValue.value === "number" || + typeof defaultValue.value === "boolean") +- ? typescript_1.default.createObjectLiteral([ +- typescript_1.default.createPropertyAssignment(typescript_1.default.createIdentifier("value"), typescript_1.default.createLiteral(defaultValue.value)), ++ ? typescript_1.default.factory.createObjectLiteralExpression([ ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createIdentifier("value"), typeof defaultValue.value === "string" ++ ? typescript_1.default.factory.createStringLiteral(defaultValue.value) ++ : // eslint-disable-next-line no-nested-ternary ++ typeof defaultValue.value === "number" ++ ? typescript_1.default.factory.createNumericLiteral(defaultValue.value) ++ : defaultValue.value ++ ? typescript_1.default.factory.createTrue() ++ : typescript_1.default.factory.createFalse()), + ]) +- : typescript_1.default.createNull()); ++ : typescript_1.default.factory.createNull()); + /** Set a property with a string value */ +- const setStringLiteralField = (fieldName, fieldValue) => typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral(fieldName), typescript_1.default.createLiteral(fieldValue)); ++ const setStringLiteralField = (fieldName, fieldValue) => typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral(fieldName), typescript_1.default.factory.createStringLiteral(fieldValue)); + /** + * ``` + * SimpleComponent.__docgenInfo.props.someProp.description = "Prop description."; +@@ -101,7 +108,7 @@ function createPropDefinition(propName, prop, options) { + * ``` + * @param required Whether prop is required or not. + */ +- const setRequired = (required) => typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral("required"), required ? typescript_1.default.createTrue() : typescript_1.default.createFalse()); ++ const setRequired = (required) => typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral("required"), required ? typescript_1.default.factory.createTrue() : typescript_1.default.factory.createFalse()); + /** + * ``` + * SimpleComponent.__docgenInfo.props.someProp.type = { +@@ -113,7 +120,7 @@ function createPropDefinition(propName, prop, options) { + */ + const setValue = (typeValue) => Array.isArray(typeValue) && + typeValue.every((value) => typeof value.value === "string") +- ? typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral("value"), typescript_1.default.createArrayLiteral(typeValue.map((value) => typescript_1.default.createObjectLiteral([ ++ ? typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral("value"), typescript_1.default.factory.createArrayLiteralExpression(typeValue.map((value) => typescript_1.default.factory.createObjectLiteralExpression([ + setStringLiteralField("value", value.value), + ])))) + : undefined; +@@ -130,9 +137,9 @@ function createPropDefinition(propName, prop, options) { + if (valueField) { + objectFields.push(valueField); + } +- return typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral(options.typePropName), typescript_1.default.createObjectLiteral(objectFields)); ++ return typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral(options.typePropName), typescript_1.default.factory.createObjectLiteralExpression(objectFields)); + }; +- return typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral(propName), typescript_1.default.createObjectLiteral([ ++ return typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral(propName), typescript_1.default.factory.createObjectLiteralExpression([ + setDefaultValue(prop.defaultValue), + setDescription(prop.description), + setName(prop.name), +@@ -158,10 +165,10 @@ function createPropDefinition(propName, prop, options) { + * @param relativeFilename Relative file path of the component source file. + */ + function insertDocgenIntoGlobalCollection(d, docgenCollectionName, relativeFilename) { +- return insertTsIgnoreBeforeStatement(typescript_1.default.createIf(typescript_1.default.createBinary(typescript_1.default.createTypeOf(typescript_1.default.createIdentifier(docgenCollectionName)), typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, typescript_1.default.createLiteral("undefined")), insertTsIgnoreBeforeStatement(typescript_1.default.createStatement(typescript_1.default.createBinary(typescript_1.default.createElementAccess(typescript_1.default.createIdentifier(docgenCollectionName), typescript_1.default.createLiteral(`${relativeFilename}#${d.displayName}`)), typescript_1.default.SyntaxKind.EqualsToken, typescript_1.default.createObjectLiteral([ +- typescript_1.default.createPropertyAssignment(typescript_1.default.createIdentifier("docgenInfo"), typescript_1.default.createPropertyAccess(typescript_1.default.createIdentifier(d.displayName), typescript_1.default.createIdentifier("__docgenInfo"))), +- typescript_1.default.createPropertyAssignment(typescript_1.default.createIdentifier("name"), typescript_1.default.createLiteral(d.displayName)), +- typescript_1.default.createPropertyAssignment(typescript_1.default.createIdentifier("path"), typescript_1.default.createLiteral(`${relativeFilename}#${d.displayName}`)), ++ return insertTsIgnoreBeforeStatement(typescript_1.default.factory.createIfStatement(typescript_1.default.factory.createBinaryExpression(typescript_1.default.factory.createTypeOfExpression(typescript_1.default.factory.createIdentifier(docgenCollectionName)), typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, typescript_1.default.factory.createStringLiteral("undefined")), insertTsIgnoreBeforeStatement(typescript_1.default.factory.createExpressionStatement(typescript_1.default.factory.createBinaryExpression(typescript_1.default.factory.createElementAccessExpression(typescript_1.default.factory.createIdentifier(docgenCollectionName), typescript_1.default.factory.createStringLiteral(`${relativeFilename}#${d.displayName}`)), typescript_1.default.SyntaxKind.EqualsToken, typescript_1.default.factory.createObjectLiteralExpression([ ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createIdentifier("docgenInfo"), typescript_1.default.factory.createPropertyAccessExpression(typescript_1.default.factory.createIdentifier(d.displayName), typescript_1.default.factory.createIdentifier("__docgenInfo"))), ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createIdentifier("name"), typescript_1.default.factory.createStringLiteral(d.displayName)), ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createIdentifier("path"), typescript_1.default.factory.createStringLiteral(`${relativeFilename}#${d.displayName}`)), + ])))))); + } + /** +@@ -180,15 +187,15 @@ function insertDocgenIntoGlobalCollection(d, docgenCollectionName, relativeFilen + * @param options Generator options. + */ + function setComponentDocGen(d, options) { +- return insertTsIgnoreBeforeStatement(typescript_1.default.createStatement(typescript_1.default.createBinary( ++ return insertTsIgnoreBeforeStatement(typescript_1.default.factory.createExpressionStatement(typescript_1.default.factory.createBinaryExpression( + // SimpleComponent.__docgenInfo +- typescript_1.default.createPropertyAccess(typescript_1.default.createIdentifier(d.displayName), typescript_1.default.createIdentifier("__docgenInfo")), typescript_1.default.SyntaxKind.EqualsToken, typescript_1.default.createObjectLiteral([ ++ typescript_1.default.factory.createPropertyAccessExpression(typescript_1.default.factory.createIdentifier(d.displayName), typescript_1.default.factory.createIdentifier("__docgenInfo")), typescript_1.default.SyntaxKind.EqualsToken, typescript_1.default.factory.createObjectLiteralExpression([ + // SimpleComponent.__docgenInfo.description +- typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral("description"), typescript_1.default.createLiteral(d.description)), ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral("description"), typescript_1.default.factory.createStringLiteral(d.description)), + // SimpleComponent.__docgenInfo.displayName +- typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral("displayName"), typescript_1.default.createLiteral(d.displayName)), ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral("displayName"), typescript_1.default.factory.createStringLiteral(d.displayName)), + // SimpleComponent.__docgenInfo.props +- typescript_1.default.createPropertyAssignment(typescript_1.default.createLiteral("props"), typescript_1.default.createObjectLiteral(Object.entries(d.props).map(([propName, prop]) => createPropDefinition(propName, prop, options)))), ++ typescript_1.default.factory.createPropertyAssignment(typescript_1.default.factory.createStringLiteral("props"), typescript_1.default.factory.createObjectLiteralExpression(Object.entries(d.props).map(([propName, prop]) => createPropDefinition(propName, prop, options)))), + ])))); + } + function generateDocgenCodeBlock(options) { +@@ -196,7 +203,7 @@ function generateDocgenCodeBlock(options) { + const relativeFilename = path_1.default + .relative("./", path_1.default.resolve("./", options.filename)) + .replace(/\\/g, "/"); +- const wrapInTryStatement = (statements) => typescript_1.default.createTry(typescript_1.default.createBlock(statements, true), typescript_1.default.createCatchClause(typescript_1.default.createVariableDeclaration(typescript_1.default.createIdentifier("__react_docgen_typescript_loader_error")), typescript_1.default.createBlock([])), undefined); ++ const wrapInTryStatement = (statements) => typescript_1.default.factory.createTryStatement(typescript_1.default.factory.createBlock(statements, true), typescript_1.default.factory.createCatchClause(typescript_1.default.factory.createVariableDeclaration(typescript_1.default.factory.createIdentifier("__react_docgen_typescript_loader_error")), typescript_1.default.factory.createBlock([])), undefined); + const codeBlocks = options.componentDocs.map((d) => wrapInTryStatement([ + options.setDisplayName ? setDisplayName(d) : null, + setComponentDocGen(d, options), +@@ -208,7 +215,7 @@ function generateDocgenCodeBlock(options) { + const printer = typescript_1.default.createPrinter({ newLine: typescript_1.default.NewLineKind.LineFeed }); + const printNode = (sourceNode) => printer.printNode(typescript_1.default.EmitHint.Unspecified, sourceNode, sourceFile); + // Concat original source code with code from generated code blocks. +- const result = codeBlocks.reduce((acc, node) => `${acc}\n${printNode(node)}`, ++ const result = codeBlocks.reduce((acc, node) => `${acc}\n${printNode(node)}`, + // Use original source text rather than using printNode on the parsed form + // to prevent issue where literals are stripped within components. + // Ref: https://github.com/strothj/react-docgen-typescript-loader/issues/7 diff --git a/.yarn/patches/mongodb-npm-4.17.1-a2fe811ff1.patch b/.yarn/patches/mongodb-npm-4.17.1-a2fe811ff1.patch new file mode 100644 index 000000000000..501881370244 --- /dev/null +++ b/.yarn/patches/mongodb-npm-4.17.1-a2fe811ff1.patch @@ -0,0 +1,13 @@ +diff --git a/mongodb.d.ts b/mongodb.d.ts +index dd080b553309594c28093365ea101adec5c0a20c..20a616de8c97ec68629c01a848ea8df4fe122bf2 100644 +--- a/mongodb.d.ts ++++ b/mongodb.d.ts +@@ -5539,7 +5539,7 @@ export declare interface MonitorOptions extends Omit = Depth['length'] extends 8 ? [] : Type extends string | number | boolean | Date | RegExp | Buffer | Uint8Array | ((...args: any[]) => any) | { ++export declare type NestedPaths = Depth['length'] extends 1 ? [] : Type extends string | number | boolean | Date | RegExp | Buffer | Uint8Array | ((...args: any[]) => any) | { + _bsontype: string; + } ? [] : Type extends ReadonlyArray ? [] | [number, ...NestedPaths] : Type extends Map ? [string] : Type extends object ? { + [Key in Extract]: Type[Key] extends Type ? [Key] : Type extends Type[Key] ? [Key] : Type[Key] extends ReadonlyArray ? Type extends ArrayType ? [Key] : ArrayType extends Type ? [Key] : [ diff --git a/_templates/package/new/package.json.ejs.t b/_templates/package/new/package.json.ejs.t index aafc1f6e88fb..950e5cb2bf62 100644 --- a/_templates/package/new/package.json.ejs.t +++ b/_templates/package/new/package.json.ejs.t @@ -7,11 +7,11 @@ to: packages/<%= name %>/package.json "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "^27.4.1", - "eslint": "^8.12.0", - "jest": "~29.5.0", + "@types/jest": "~29.5.3", + "eslint": "~8.45.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.0.2" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/_templates/service/new/package.json.ejs.t b/_templates/service/new/package.json.ejs.t index d3040640a687..2c74278d1ced 100644 --- a/_templates/service/new/package.json.ejs.t +++ b/_templates/service/new/package.json.ejs.t @@ -20,29 +20,29 @@ to: ee/apps/<%= name %>/package.json "dependencies": { "@rocket.chat/core-services": "workspace:^", "@rocket.chat/core-typings": "workspace:^", - "@rocket.chat/emitter": "0.31.22", + "@rocket.chat/emitter": "next", "@rocket.chat/model-typings": "workspace:^", "@rocket.chat/models": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", - "@rocket.chat/string-helpers": "0.31.22", - "@types/node": "^14.18.21", - "ejson": "^2.2.2", + "@rocket.chat/string-helpers": "next", + "@types/node": "^14.18.51", + "ejson": "^2.2.3", "eventemitter3": "^4.0.7", "fibers": "^5.0.3", "mem": "^8.1.1", - "moleculer": "^0.14.21", + "moleculer": "^0.14.29", "mongodb": "^4.12.1", "nats": "^2.4.0", - "pino": "^8.4.2", + "pino": "^8.15.0", "polka": "^0.5.2" }, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/eslint": "^8.4.10", + "@types/eslint": "~8.44.0", "@types/polka": "^0.5.4", - "eslint": "^8.29.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~4.6.4" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/<%= name %>/src/service.js", "files": [ diff --git a/apps/meteor/.babelrc b/apps/meteor/.babelrc index a8c20b400ca5..382b93318fab 100644 --- a/apps/meteor/.babelrc +++ b/apps/meteor/.babelrc @@ -1,9 +1,15 @@ { - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ], - "plugins": [ - "babel-plugin-istanbul" - ] + "presets": ["@babel/preset-env", "@babel/preset-react"], + "env": { + "coverage": { + "plugins": [ + [ + "istanbul", + { + "exclude": ["**/*.spec.js", "**/*.test.js"] + } + ] + ] + } + } } diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index 62a0476d9077..003baa57aa8b 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -15,6 +15,11 @@ RUN set -x \ && npm install sharp@0.30.4 \ && mv node_modules/sharp npm/node_modules/sharp \ # End hack for sharp + # Start hack for isolated-vm... + && rm -rf npm/node_modules/isolated-vm \ + && npm install isolated-vm@4.4.2 \ + && mv node_modules/isolated-vm npm/node_modules/isolated-vm \ + # End hack for isolated-vm && cd npm \ && npm rebuild bcrypt --build-from-source \ && npm cache clear --force \ diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index 6a03bde90b43..96a1740fa587 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.3.8 +ENV RC_VERSION 6.4.0-rc.5 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/.eslintignore b/apps/meteor/.eslintignore index 158360ed08eb..2bbdbae00b89 100644 --- a/apps/meteor/.eslintignore +++ b/apps/meteor/.eslintignore @@ -1,23 +1,15 @@ -node_modules -data/ -tests/e2e/test-failures/ -packages/autoupdate/ -packages/meteor-streams/ -app/emoji-emojione/generateEmojiIndex.js -packages/rocketchat-livechat/assets/rocketchat-livechat.min.js -packages/rocketchat-livechat/assets/rocket-livechat.js -app/theme/client/vendor/ -public/packages/rocketchat_videobridge/client/public/external_api.js -packages/tap-i18n/lib/tap_i18next/tap_i18next-1.7.3.js -private/moment-locales/ -public/livechat/ -public/pdf.worker.min.js -public/workers/**/* -imports/client/**/* -ee/server/services/dist/** +/node_modules/ +#/tests/e2e/ +/tests/data/ +/packages/ +/app/emoji-emojione/generateEmojiIndex.js +/public/ +/private/moment-locales/ +/imports/ +/ee/server/services/dist/ !/.mocharc.js !/.mocharc.*.js !/.scripts/ -!/.storybook/ +#!/.storybook/ !/client/.eslintrc.js !/ee/client/.eslintrc.js diff --git a/apps/meteor/.eslintrc.json b/apps/meteor/.eslintrc.json index 31d63e993611..78139c551069 100644 --- a/apps/meteor/.eslintrc.json +++ b/apps/meteor/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": ["@rocket.chat/eslint-config", "plugin:you-dont-need-lodash-underscore/compatible"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react", "plugin:you-dont-need-lodash-underscore/compatible"], "globals": { "__meteor_bootstrap__": false, "__meteor_runtime_config__": false, @@ -7,38 +7,70 @@ "chrome": false, "jscolor": false }, - "plugins": ["react", "react-hooks"], "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react-hooks/rules-of-hooks": "error", + "import/named": "error", "react-hooks/exhaustive-deps": [ "warn", { "additionalHooks": "(useComponentDidUpdate)" } + ], + "prefer-arrow-callback": [ + "error", + { + "allowNamedFunctions": true + } ] }, - "settings": { - "react": { - "version": "detect" - } - }, "overrides": [ { "files": ["**/*.ts", "**/*.tsx"], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "plugins": ["react"], "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": ["function", "parameter", "variable"], + "modifiers": ["destructured"], + "format": null + }, + { + "selector": ["variable"], + "format": ["camelCase", "UPPER_CASE", "PascalCase"], + "leadingUnderscore": "allowSingleOrDouble" + }, + { + "selector": ["function"], + "format": ["camelCase", "PascalCase"], + "leadingUnderscore": "allowSingleOrDouble" + }, + { + "selector": ["parameter"], + "format": ["PascalCase"], + "filter": { + "regex": "Component$", + "match": true + } + }, + { + "selector": ["parameter"], + "format": ["camelCase"], + "leadingUnderscore": "allow" + }, + { + "selector": ["parameter"], + "format": ["camelCase"], + "modifiers": ["unused"], + "leadingUnderscore": "require" + }, + { + "selector": ["interface"], + "format": ["PascalCase"], + "custom": { + "regex": "^I[A-Z]", + "match": true + } + } + ], "@typescript-eslint/no-misused-promises": [ "error", { @@ -53,18 +85,53 @@ "parserOptions": { "project": ["./tsconfig.json"] }, - "excludedFiles": [".scripts/*.ts"], - "settings": { - "react": { - "version": "detect" - } - } + "excludedFiles": [".scripts/*.ts"] }, { "files": ["**/*.tests.js", "**/*.tests.ts", "**/*.spec.ts"], "env": { "mocha": true } + }, + { + "files": ["**/*.spec.ts", "**/*.spec.tsx"], + "extends": ["plugin:testing-library/react"], + "rules": { + "testing-library/no-await-sync-events": "warn", + "testing-library/no-manual-cleanup": "warn", + "testing-library/prefer-explicit-assert": "warn", + "testing-library/prefer-user-event": "warn" + }, + "env": { + "mocha": true + } + }, + { + "files": ["**/*.stories.js", "**/*.stories.jsx", "**/*.stories.ts", "**/*.stories.tsx", "**/*.spec.tsx"], + "rules": { + "react/display-name": "off", + "react/no-multi-comp": "off" + } + }, + { + "files": ["**/*.stories.ts", "**/*.stories.tsx"], + "rules": { + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off" + } + }, + { + "files": ["client/**/*.ts", "client/**/*.tsx", "ee/client/**/*.ts", "ee/client/**/*.tsx"], + "rules": { + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/no-floating-promises": "off" + } + }, + { + "files": ["**/*.d.ts"], + "rules": { + "@typescript-eslint/naming-convention": "off" + } } ] } diff --git a/apps/meteor/.gitignore b/apps/meteor/.gitignore index 287cb313c174..a9fd54ab8711 100644 --- a/apps/meteor/.gitignore +++ b/apps/meteor/.gitignore @@ -87,4 +87,5 @@ out.txt dist *-session.json matrix-federation-config/* -.eslintcache \ No newline at end of file +.eslintcache +tsconfig.typecheck.tsbuildinfo diff --git a/apps/meteor/.meteor/packages b/apps/meteor/.meteor/packages index 1a59026a80d3..ae788af78034 100644 --- a/apps/meteor/.meteor/packages +++ b/apps/meteor/.meteor/packages @@ -63,7 +63,6 @@ reactive-dict@1.3.1 reactive-var@1.0.12 babel-compiler@7.10.4 -standard-minifier-js@2.8.1 standard-minifier-css@1.9.2 dynamic-import@0.7.3 ecmascript@0.16.7 @@ -75,3 +74,4 @@ autoupdate@1.8.0 jquery zodern:types +zodern:standard-minifier-js diff --git a/apps/meteor/.meteor/versions b/apps/meteor/.meteor/versions index 5949c2258e3b..66f61e2cd8cc 100644 --- a/apps/meteor/.meteor/versions +++ b/apps/meteor/.meteor/versions @@ -52,7 +52,6 @@ meteor-base@1.5.1 meteor-developer-oauth@1.3.2 meteorhacks:inject-initial@1.0.5 minifier-css@1.6.4 -minifier-js@2.7.5 minimongo@1.9.3 modern-browsers@0.1.9 modules@0.19.0 @@ -92,7 +91,6 @@ sha@1.0.9 shell-server@0.5.0 socket-stream-client@0.5.1 standard-minifier-css@1.9.2 -standard-minifier-js@2.8.1 tracker@1.3.2 twitter-oauth@1.3.3 typescript@4.9.4 @@ -100,4 +98,6 @@ underscore@1.0.13 url@1.3.2 webapp@1.13.5 webapp-hashing@1.1.1 +zodern:caching-minifier@0.5.0 +zodern:standard-minifier-js@5.1.2 zodern:types@1.0.9 diff --git a/apps/meteor/.mocharc.api.js b/apps/meteor/.mocharc.api.js index 92168e92cc9a..eca1284e62e5 100644 --- a/apps/meteor/.mocharc.api.js +++ b/apps/meteor/.mocharc.api.js @@ -9,5 +9,5 @@ module.exports = { timeout: 10000, bail: true, file: 'tests/end-to-end/teardown.js', - spec: ['tests/unit/app/api/server/v1/**/*.spec.*', 'tests/end-to-end/api/**/*', 'tests/end-to-end/apps/*'], + spec: ['tests/end-to-end/api/**/*', 'tests/end-to-end/apps/*'], }; diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 7eff846f683c..cf339a420378 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -33,10 +33,10 @@ module.exports = { exit: false, slow: 200, spec: [ - 'client/**/*.spec.{ts,tsx}', - 'tests/unit/client/**/*.spec.{ts,tsx}', + 'tests/unit/client/sidebar/**/*.spec.{ts,tsx}', + 'tests/unit/client/components/**/*.spec.{ts,tsx}', + 'tests/unit/client/lib/**/*.spec.{ts,tsx}', 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts', ], - exclude: ['client/hooks/*.spec.{ts,tsx}'], }; diff --git a/apps/meteor/.scripts/translationDiff.js b/apps/meteor/.scripts/translationDiff.js index b61ef469a7f4..7c83e33c76ee 100644 --- a/apps/meteor/.scripts/translationDiff.js +++ b/apps/meteor/.scripts/translationDiff.js @@ -1,7 +1,7 @@ #!/usr/bin/env node -const path = require('path'); const fs = require('fs'); +const path = require('path'); const util = require('util'); // Convert fs.readFile into Promise version of same diff --git a/apps/meteor/.storybook/decorators.tsx b/apps/meteor/.storybook/decorators.tsx index a295c4ab60b8..f0211331e4bc 100644 --- a/apps/meteor/.storybook/decorators.tsx +++ b/apps/meteor/.storybook/decorators.tsx @@ -1,3 +1,4 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; import type { DecoratorFunction } from '@storybook/addons'; import type { ReactElement } from 'react'; import React from 'react'; @@ -8,6 +9,8 @@ import RouterContextMock from '../client/stories/contexts/RouterContextMock'; import ServerContextMock from '../client/stories/contexts/ServerContextMock'; import TranslationContextMock from '../client/stories/contexts/TranslationContextMock'; +const MockedAppRoot = mockAppRoot().build(); + export const rocketChatDecorator: DecoratorFunction> = (fn, { parameters }) => { const linkElement = document.getElementById('theme-styles') || document.createElement('link'); if (linkElement.id !== 'theme-styles') { @@ -25,7 +28,7 @@ export const rocketChatDecorator: DecoratorFunction> = (fn const { default: icons } = require('!!raw-loader!../private/public/icons.svg'); return ( - + @@ -41,6 +44,6 @@ export const rocketChatDecorator: DecoratorFunction> = (fn - + ); }; diff --git a/apps/meteor/.storybook/main.js b/apps/meteor/.storybook/main.js index 223c31d6bb11..0e0b6db7c0e9 100644 --- a/apps/meteor/.storybook/main.js +++ b/apps/meteor/.storybook/main.js @@ -25,6 +25,20 @@ module.exports = { reactDocgen: 'react-docgen-typescript-plugin', }, webpackFinal: async (config) => { + // Those aliases are needed because dependencies in the monorepo use another + // dependencies that are not hoisted on this workspace + config.resolve.alias = { + 'react$': require.resolve('../../../node_modules/react'), + // 'react/jsx-runtime': require.resolve('../../../node_modules/react/jsx-runtime'), + '@tanstack/react-query': require.resolve('../../../node_modules/@tanstack/react-query'), + }; + + config.module.rules.push({ + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }); + const cssRule = config.module.rules.find(({ test }) => test.test('index.css')); cssRule.use[2].options = { diff --git a/apps/meteor/.storybook/preview.ts b/apps/meteor/.storybook/preview.ts index 2b1ed2526995..109a66ee882c 100644 --- a/apps/meteor/.storybook/preview.ts +++ b/apps/meteor/.storybook/preview.ts @@ -1,11 +1,12 @@ -import { DocsPage, DocsContainer } from '@storybook/addon-docs'; -import { addDecorator, addParameters } from '@storybook/react'; +import { DecoratorFn, Parameters } from '@storybook/react'; import { rocketChatDecorator } from './decorators'; -addDecorator(rocketChatDecorator); +export const decorators: DecoratorFn[] = [ + rocketChatDecorator +]; -addParameters({ +export const parameters: Parameters = { backgrounds: { grid: { cellSize: 4, @@ -13,14 +14,10 @@ addParameters({ opacity: 0.5, }, }, - docs: { - container: DocsContainer, - page: DocsPage, - }, options: { storySort: { method: 'alphabetical', order: ['Components', '*', 'Enterprise'], }, }, -}); +}; diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index 76b1bde2a41d..da842431ab0b 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,302 @@ # @rocket.chat/meteor +## 6.4.0-rc.5 + +### Minor Changes + +- 1041d4d361: Added option to select between two script engine options for the integrations + +### Patch Changes + +- Bump @rocket.chat/meteor version. +- ec60dbe8f5: Fixed custom translations not being displayed +- Updated dependencies [1041d4d361] + - @rocket.chat/core-typings@6.4.0-rc.5 + - @rocket.chat/rest-typings@6.4.0-rc.5 + - @rocket.chat/tools@0.1.0-rc.0 + - @rocket.chat/api-client@0.1.9-rc.5 + - @rocket.chat/omnichannel-services@0.0.15-rc.5 + - @rocket.chat/pdf-worker@0.0.15-rc.5 + - @rocket.chat/presence@0.0.15-rc.5 + - @rocket.chat/core-services@0.2.0-rc.5 + - @rocket.chat/cron@0.0.11-rc.5 + - @rocket.chat/gazzodown@2.0.0-rc.5 + - @rocket.chat/model-typings@0.1.0-rc.5 + - @rocket.chat/ui-contexts@2.0.0-rc.5 + - @rocket.chat/fuselage-ui-kit@2.0.0-rc.5 + - @rocket.chat/models@0.0.15-rc.5 + - @rocket.chat/ui-theming@0.1.0-rc.0 + - @rocket.chat/ui-client@2.0.0-rc.5 + - @rocket.chat/ui-video-conf@2.0.0-rc.5 + - @rocket.chat/web-ui-registration@2.0.0-rc.5 + - @rocket.chat/instance-status@0.0.15-rc.5 + +## 6.4.0-rc.4 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + - @rocket.chat/core-typings@6.4.0-rc.4 + - @rocket.chat/rest-typings@6.4.0-rc.4 + - @rocket.chat/api-client@0.1.8-rc.4 + - @rocket.chat/omnichannel-services@0.0.14-rc.4 + - @rocket.chat/pdf-worker@0.0.14-rc.4 + - @rocket.chat/presence@0.0.14-rc.4 + - @rocket.chat/core-services@0.2.0-rc.4 + - @rocket.chat/cron@0.0.10-rc.4 + - @rocket.chat/gazzodown@2.0.0-rc.4 + - @rocket.chat/model-typings@0.1.0-rc.4 + - @rocket.chat/ui-contexts@2.0.0-rc.4 + - @rocket.chat/fuselage-ui-kit@2.0.0-rc.4 + - @rocket.chat/models@0.0.14-rc.4 + - @rocket.chat/ui-theming@0.1.0-rc.0 + - @rocket.chat/ui-client@2.0.0-rc.4 + - @rocket.chat/ui-video-conf@2.0.0-rc.4 + - @rocket.chat/web-ui-registration@2.0.0-rc.4 + - @rocket.chat/instance-status@0.0.14-rc.4 + +## 6.4.0-rc.3 + +### Patch Changes + +- Bump @rocket.chat/meteor version. +- 614a9b8fc8: Show correct date for last day time +- 61a106fbf2: Increase cron job check delay to 1 min from 5s. + + This reduces MongoDB requests introduced on 6.3. + +- Updated dependencies [d9a150000d] +- Updated dependencies [61a106fbf2] + - @rocket.chat/presence@0.0.13-rc.3 + - @rocket.chat/cron@0.0.9-rc.3 + - @rocket.chat/core-typings@6.4.0-rc.3 + - @rocket.chat/rest-typings@6.4.0-rc.3 + - @rocket.chat/api-client@0.1.7-rc.3 + - @rocket.chat/omnichannel-services@0.0.13-rc.3 + - @rocket.chat/pdf-worker@0.0.13-rc.3 + - @rocket.chat/core-services@0.2.0-rc.3 + - @rocket.chat/gazzodown@2.0.0-rc.3 + - @rocket.chat/model-typings@0.1.0-rc.3 + - @rocket.chat/ui-contexts@2.0.0-rc.3 + - @rocket.chat/fuselage-ui-kit@2.0.0-rc.3 + - @rocket.chat/models@0.0.13-rc.3 + - @rocket.chat/ui-theming@0.1.0-rc.0 + - @rocket.chat/ui-client@2.0.0-rc.3 + - @rocket.chat/ui-video-conf@2.0.0-rc.3 + - @rocket.chat/web-ui-registration@2.0.0-rc.3 + - @rocket.chat/instance-status@0.0.13-rc.3 + +## 6.4.0-rc.2 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + - @rocket.chat/core-typings@6.4.0-rc.2 + - @rocket.chat/rest-typings@6.4.0-rc.2 + - @rocket.chat/api-client@0.1.7-rc.2 + - @rocket.chat/omnichannel-services@0.0.13-rc.2 + - @rocket.chat/pdf-worker@0.0.13-rc.2 + - @rocket.chat/presence@0.0.13-rc.2 + - @rocket.chat/core-services@0.2.0-rc.2 + - @rocket.chat/cron@0.0.9-rc.2 + - @rocket.chat/gazzodown@2.0.0-rc.2 + - @rocket.chat/model-typings@0.1.0-rc.2 + - @rocket.chat/ui-contexts@2.0.0-rc.2 + - @rocket.chat/fuselage-ui-kit@2.0.0-rc.2 + - @rocket.chat/models@0.0.13-rc.2 + - @rocket.chat/ui-theming@0.1.0-rc.0 + - @rocket.chat/ui-client@2.0.0-rc.2 + - @rocket.chat/ui-video-conf@2.0.0-rc.2 + - @rocket.chat/web-ui-registration@2.0.0-rc.2 + - @rocket.chat/instance-status@0.0.13-rc.2 + +## 6.4.0-rc.1 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + - @rocket.chat/core-typings@6.4.0-rc.1 + - @rocket.chat/rest-typings@6.4.0-rc.1 + - @rocket.chat/api-client@0.1.5-rc.1 + - @rocket.chat/omnichannel-services@0.0.11-rc.1 + - @rocket.chat/pdf-worker@0.0.11-rc.1 + - @rocket.chat/presence@0.0.11-rc.1 + - @rocket.chat/core-services@0.2.0-rc.1 + - @rocket.chat/cron@0.0.7-rc.1 + - @rocket.chat/gazzodown@2.0.0-rc.1 + - @rocket.chat/model-typings@0.1.0-rc.1 + - @rocket.chat/ui-contexts@2.0.0-rc.1 + - @rocket.chat/fuselage-ui-kit@2.0.0-rc.1 + - @rocket.chat/models@0.0.11-rc.1 + - @rocket.chat/ui-theming@0.1.0-rc.0 + - @rocket.chat/ui-client@2.0.0-rc.1 + - @rocket.chat/ui-video-conf@2.0.0-rc.1 + - @rocket.chat/web-ui-registration@2.0.0-rc.1 + - @rocket.chat/instance-status@0.0.11-rc.1 + +## 6.4.0-rc.0 + +### Minor Changes + +- 239a34e877: new: ring mobile users on direct conference calls +- 04fe492555: Added new Omnichannel's trigger condition "After starting a chat". +- 4186eecf05: Introduce the ability to report an user +- 92b690d206: fix: Wrong toast message while creating a new custom sound with an existing name +- f83ea5d6e8: Added support for threaded conversation in Federated rooms. +- 682d0bc05a: fix: Time format of Retention Policy +- 1b42dfc6c1: Added a new Roles bridge to RC Apps-Engine for reading and retrieving role details. +- 2db32f0d4a: Add option to select what URL previews should be generated for each message. +- 982ef6f459: Add new event to notify users directly about new banners +- 19aec23cda: New AddUser workflow for Federated Rooms +- ebab8c4dd8: Added Reports Metrics Dashboard to Omnichannel +- 85a936220c: feat: remove enforce password fallback dependency +- 5832be2e1b: Reorganized the message menu +- 074db3b419: UX improvement for the Moderation Console Context bar for viewing the reported messages. The Report reason is now displayed in the reported messages context bar. + The Moderation Action Modal confirmation description is updated to be more clear and concise. +- 357a3a50fa: feat: high-contrast theme +- 7070f00b05: feat: return all broken password policies at once +- ead7c7bef2: Fixed read receipts not getting deleted after corresponding message is deleted +- ad08c26b46: Introduced upsells for the engagement dashboard and device management admin sidebar items in CE workspaces. Additionally, restructured the admin sidebar items to enhance organization. +- 93d4912e17: fix: missing params on updateOwnBasicInfo endpoint +- ee3815fce4: feat: add ChangePassword field to Account/Security +- 1000b9b317: Fixed the issue of apps icon uneven alignment in case of missing icons inside message composer toolbar & message toolbar menu. + +### Patch Changes + +- 6d453f71ac: Translation files are requested multiple times +- cada29b6ce: fix: Managers allowed to make deactivated agent's available +- 470c29d7e9: Fixed an issue causing `queue time` to be calculated from current time when a room was closed without being served. + Now: + - For served rooms: queue time = servedBy time - queuedAt + - For not served, but open rooms = now - queuedAt + - For not served and closed rooms = closedAt - queuedAt +- ea8998602b: fix: Performance issue on `Messages.countByType` aggregation caused by unindexed property on messages collection +- a08006c9f0: feat: add sections to room header and user infos menus with menuV2 +- 203304782f: Fixed `overrideDestinationChannelEnabled` treated as a required param in `integrations.create` and `integration.update` endpoints +- 9edca67b9b: feat(apps): `ActionManagerBusyState` component for apps `ui.interaction` +- 6fa30ddcd1: Hide Reset TOTP option if 2FA is disabled +- ff7e181464: Added ability to freeze or completely disable integration scripts through envvars +- 4ce8ea89a8: fix: custom emoji upload with FileSystem method +- 87570d0fb7: New filters to the Rooms Table at `Workspace > Rooms` +- 8a59855fcf: When setting a room as read-only, do not allow previously unmuted users to send messages. +- c73f5373b8: fix: finnish translation +- f5a886a144: fixed an issue where 2fa was not working after an OAuth redirect +- 459c8574ed: Fixed issue with custom OAuth services' settings not being be fully removed +- 42644a6e44: fix: Prevent `RoomProvider.useEffect` from subscribing to room-data stream multiple times +- 9bdbc9b086: load sounds right before playing them +- 6154979119: Fix users being created without the `roles` field +- 6bcdd88531: Fixed CAS login after popup closes +- 839789c988: Fix moment timestamps language change +- f0025d4d92: Fixed message fetching method in LivechatBridge for Apps +- 9c957b9d9a: Fix pruning messages in a room results in an incorrect message counter +- 583a3149fe: fix: rejected conference calls continue to ring +- b59fd5d7fb: User information crashing for some locales +- 4349443629: Fix performance issue on Engagement Dashboard aggregation +- 69447e1864: Added ability to disable private app installation via envvar (DISABLE_PRIVATE_APP_INSTALLATION) +- 52a1aa94eb: improve: System messages for omni-visitor abandonment feature +- 7dffec2e2f: chore: Add danger variant to apps action button menus +- f0c8867bb9: Disabled call to tags enterprise endpoint when on community license +- 5e89694bfa: Fixes SAML full name updates not being mirrored to DM rooms. +- d6f0c6afe2: Fixed Importer Progress Bar progress indicator +- 177506ea91: Make user default role setting public +- 3fb2124166: Fixed misleading of 'total' in team members list inside Channel +- 5cee21468e: Fix spotlight search does not find rooms with special or non-latin characters +- cf59c8abe3: Fix engagement dashboard not showing data +- dfb9a075b3: fixed wrong user status displayed during mentioning a user in a channel +- 1fbbb6241a: Don't allow to report self messages +- 53e0c346e2: fixed scrollbar over content in Federated Room List +- 5321e87363: Fix seat counter including bots users +- 7137a193a7: feat: Add flag to disable teams mention via troubleshoot page +- 59e6fe3d2a: fixed layout changing from embedded view when navigating +- 3245a0a318: Fix LinkedIn OAuth broken +- 45a8943ed4: Removed old/deprecated Rocket.Chat Federation card from Info page +- 6eea189ec8: Fix the code that was setting email URL to an invalid value when SMTP was not set +- f5a886a144: fixed an issue where oauth login was not working with some providers +- ba24f3c21f: Fixed `default` field not being returned from the `setDefault` endpoints when setting to false +- a79f61461d: Fixed an issue where timeout for http requests in Apps-Engine bridges was too short +- 51b988b3df: Fix importer filters not working +- 5d857f462c: fix: stop blinking "Room not found" before dm creation +- db26f8a8ee: fixed an issue with the positioning of the message menu +- aaefe865a7: fix: agent role being removed upon user deactivation +- 306a5830c3: Fix `mention-here` and `mention-all` permissions not being honored +- 761cad4382: Fix CORS headers not being set for assets +- 9e5718002a: Fixed Slackbridge was not handling correctly received events from Slack anymore. Events: (Send, edit, delete, react meassages) +- 54ef89c9a7: fix: show requested filters only on requested apps view +- 1589279b79: Fix users not able to login after block time perdiod has passed +- 880ab5689c: Fixed selected departments not being displayed due to pagination +- a81bad24e0: Fixed Apps-Engine event `IPostUserCreated` execution +- 7a4fdf41f8: Fix validation in app status call that allowed Enterprise apps to be enabled in invalid environments +- e28f8d95f0: Fixed inviter not informed when inviting member to room via `/invite` slashcommand +- d47d2021ac: Fixed "teams" icon not being displayed on spotlight sidebar search +- 93d5a5ceb8: fix: User timezone not being respected on Current Chat's filter +- f556518fa1: Change SAU aggregation to consider only sessions from few days ago instead of the whole past. + + This is particularly important for large workspaces in case the cron job did not run for some time, in that case the amount of sessions would accumulate and the aggregation would take a long time to run. + +- b747f3d3bc: Fixed unable to create admin user using ADMIN\_\* environment variables +- 2cf2643399: Fixed failing user data exports +- ace35997a6: chore: Increase cache time from 5s to 10s on `getUnits` helpers. This should reduce the number of DB calls made by this method to fetch the unit limitations for a user. +- f5a886a144: fixed an issue on oauth login that caused missing emails to be detected as changed data +- 61128364d6: Fixes a problem where the calculated time for considering the visitor abandonment was the first message from the visitor and not the visitor's reply to the agent. +- 9496f1eb97: Deprecate `livechat:getOverviewData` and `livechat:getAgentOverviewData` methods and create API endpoints `livechat/analytics/overview` and `livechat/analytics/agent-overview` to fetch analytics data +- 01dec055a0: Fixed Accounts profile form name change was not working +- e4837a15ed: Fixed user mentioning when prepending the username with `>` +- d45365436e: Use group filter when set to LDAP sync process +- c536a4a237: fix: Missing padding on Omnichannel contacts Contextualbar loading state +- 87e4a4aa56: Fixes a problem that allowed users to send empty spaces as comment to bypass the "comment required" setting +- 69a5213afc: Fixed an issue where a mailer error was being sent to customers using offline message's form on Omnichannel instead of the translated one +- b8f3d5014f: Fixed the login page language switcher, now the component has a new look, is reactive and the language selection becomes concrete upon login in. Also changed the default language of the login page to be the browser language. +- 22cf158c43: fixed the unread messages mark not showing +- 72a34a02f7: fixed the video recorder window not closing after permission is denied. +- Updated dependencies [239a34e877] +- Updated dependencies [203304782f] +- Updated dependencies [1246a21648] +- Updated dependencies [4186eecf05] +- Updated dependencies [8a59855fcf] +- Updated dependencies [f9a748526d] +- Updated dependencies [5cee21468e] +- Updated dependencies [dc1d8ce92e] +- Updated dependencies [2db32f0d4a] +- Updated dependencies [982ef6f459] +- Updated dependencies [ba24f3c21f] +- Updated dependencies [19aec23cda] +- Updated dependencies [ebab8c4dd8] +- Updated dependencies [aaefe865a7] +- Updated dependencies [074db3b419] +- Updated dependencies [357a3a50fa] +- Updated dependencies [f556518fa1] +- Updated dependencies [ead7c7bef2] +- Updated dependencies [61128364d6] +- Updated dependencies [9496f1eb97] +- Updated dependencies [dce4a829fa] +- Updated dependencies [d45365436e] +- Updated dependencies [b8f3d5014f] +- Updated dependencies [93d4912e17] +- Updated dependencies [ee3815fce4] + - @rocket.chat/core-typings@6.4.0-rc.0 + - @rocket.chat/rest-typings@6.4.0-rc.0 + - @rocket.chat/fuselage-ui-kit@2.0.0-rc.0 + - @rocket.chat/model-typings@0.1.0-rc.0 + - @rocket.chat/core-services@0.2.0-rc.0 + - @rocket.chat/ui-client@2.0.0-rc.0 + - @rocket.chat/ui-contexts@2.0.0-rc.0 + - @rocket.chat/ui-theming@0.1.0-rc.0 + - @rocket.chat/i18n@0.0.2-rc.0 + - @rocket.chat/web-ui-registration@2.0.0-rc.0 + - @rocket.chat/api-client@0.1.5-rc.0 + - @rocket.chat/omnichannel-services@0.0.11-rc.0 + - @rocket.chat/pdf-worker@0.0.11-rc.0 + - @rocket.chat/presence@0.0.11-rc.0 + - @rocket.chat/cron@0.0.7-rc.0 + - @rocket.chat/gazzodown@2.0.0-rc.0 + - @rocket.chat/models@0.0.11-rc.0 + - @rocket.chat/ui-video-conf@2.0.0-rc.0 + - @rocket.chat/base64@1.0.12 + - @rocket.chat/instance-status@0.0.11-rc.0 + - @rocket.chat/random@1.2.1 + - @rocket.chat/sha256@1.0.9 + - @rocket.chat/ui-composer@0.0.1 + ## 6.3.8 ### Patch Changes diff --git a/apps/meteor/app/2fa/client/TOTPCrowd.js b/apps/meteor/app/2fa/client/TOTPCrowd.js index 57bd045b9c07..6b4e55a85211 100644 --- a/apps/meteor/app/2fa/client/TOTPCrowd.js +++ b/apps/meteor/app/2fa/client/TOTPCrowd.js @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import '../../crowd/client/index'; -import { reportError } from '../../../client/lib/2fa/utils'; import { overrideLoginMethod } from '../../../client/lib/2fa/overrideLoginMethod'; +import { reportError } from '../../../client/lib/2fa/utils'; Meteor.loginWithCrowdAndTOTP = function (username, password, code, callback) { const loginRequest = { diff --git a/apps/meteor/app/2fa/client/TOTPGoogle.js b/apps/meteor/app/2fa/client/TOTPGoogle.js index e43132684dd7..bb1e509a46d7 100644 --- a/apps/meteor/app/2fa/client/TOTPGoogle.js +++ b/apps/meteor/app/2fa/client/TOTPGoogle.js @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { Google } from 'meteor/google-oauth'; +import { Meteor } from 'meteor/meteor'; import { overrideLoginMethod } from '../../../client/lib/2fa/overrideLoginMethod'; diff --git a/apps/meteor/app/2fa/client/TOTPLDAP.js b/apps/meteor/app/2fa/client/TOTPLDAP.js index 545c7a4dd5c1..f3b833d04a72 100644 --- a/apps/meteor/app/2fa/client/TOTPLDAP.js +++ b/apps/meteor/app/2fa/client/TOTPLDAP.js @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import '../../../client/startup/ldap'; -import { reportError } from '../../../client/lib/2fa/utils'; import { overrideLoginMethod } from '../../../client/lib/2fa/overrideLoginMethod'; +import { reportError } from '../../../client/lib/2fa/utils'; Meteor.loginWithLDAPAndTOTP = function (...args) { // Pull username and password diff --git a/apps/meteor/app/2fa/client/TOTPOAuth.js b/apps/meteor/app/2fa/client/TOTPOAuth.js index c05ca52e8268..47c5e70998b6 100644 --- a/apps/meteor/app/2fa/client/TOTPOAuth.js +++ b/apps/meteor/app/2fa/client/TOTPOAuth.js @@ -1,17 +1,17 @@ import { capitalize } from '@rocket.chat/string-helpers'; -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { Facebook } from 'meteor/facebook-oauth'; import { Github } from 'meteor/github-oauth'; -import { Twitter } from 'meteor/twitter-oauth'; +import { Meteor } from 'meteor/meteor'; import { MeteorDeveloperAccounts } from 'meteor/meteor-developer-oauth'; -import { Linkedin } from 'meteor/pauli:linkedin-oauth'; import { OAuth } from 'meteor/oauth'; +import { Linkedin } from 'meteor/pauli:linkedin-oauth'; +import { Twitter } from 'meteor/twitter-oauth'; +import { overrideLoginMethod } from '../../../client/lib/2fa/overrideLoginMethod'; import { process2faReturn } from '../../../client/lib/2fa/process2faReturn'; -import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; import { convertError } from '../../../client/lib/2fa/utils'; -import { overrideLoginMethod } from '../../../client/lib/2fa/overrideLoginMethod'; +import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; let lastCredentialToken = null; let lastCredentialSecret = null; diff --git a/apps/meteor/app/2fa/client/TOTPPassword.js b/apps/meteor/app/2fa/client/TOTPPassword.js index c3eceab2a9aa..3fccb646bad1 100644 --- a/apps/meteor/app/2fa/client/TOTPPassword.js +++ b/apps/meteor/app/2fa/client/TOTPPassword.js @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { t } from '../../utils/lib/i18n'; import { process2faReturn } from '../../../client/lib/2fa/process2faReturn'; import { isTotpInvalidError, reportError } from '../../../client/lib/2fa/utils'; import { dispatchToastMessage } from '../../../client/lib/toast'; +import { t } from '../../utils/lib/i18n'; Meteor.loginWithPasswordAndTOTP = function (selector, password, code, callback) { if (typeof selector === 'string') { diff --git a/apps/meteor/app/2fa/client/TOTPSaml.js b/apps/meteor/app/2fa/client/TOTPSaml.js index 01f19f1df9d0..7d9ec34541df 100644 --- a/apps/meteor/app/2fa/client/TOTPSaml.js +++ b/apps/meteor/app/2fa/client/TOTPSaml.js @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import '../../meteor-accounts-saml/client/saml_client'; -import { reportError } from '../../../client/lib/2fa/utils'; import { overrideLoginMethod } from '../../../client/lib/2fa/overrideLoginMethod'; +import { reportError } from '../../../client/lib/2fa/utils'; Meteor.loginWithSamlTokenAndTOTP = function (credentialToken, code, callback) { Accounts.callLoginMethod({ diff --git a/apps/meteor/app/2fa/client/overrideMeteorCall.ts b/apps/meteor/app/2fa/client/overrideMeteorCall.ts index ab054c2df850..e373c8a421be 100644 --- a/apps/meteor/app/2fa/client/overrideMeteorCall.ts +++ b/apps/meteor/app/2fa/client/overrideMeteorCall.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; -import { t } from '../../utils/lib/i18n'; import { process2faReturn, process2faAsyncReturn } from '../../../client/lib/2fa/process2faReturn'; import { isTotpInvalidError } from '../../../client/lib/2fa/utils'; +import { t } from '../../utils/lib/i18n'; const { call, callAsync } = Meteor; diff --git a/apps/meteor/app/2fa/server/MethodInvocationOverride.js b/apps/meteor/app/2fa/server/MethodInvocationOverride.js index 19b37e041fd1..e17640201910 100644 --- a/apps/meteor/app/2fa/server/MethodInvocationOverride.js +++ b/apps/meteor/app/2fa/server/MethodInvocationOverride.js @@ -1,5 +1,5 @@ -import { DDPCommon } from 'meteor/ddp-common'; import { DDP } from 'meteor/ddp'; +import { DDPCommon } from 'meteor/ddp-common'; class MethodInvocation extends DDPCommon.MethodInvocation { constructor(options) { diff --git a/apps/meteor/app/2fa/server/code/EmailCheck.ts b/apps/meteor/app/2fa/server/code/EmailCheck.ts index 166ae1a024e5..bf2e4aa170c2 100644 --- a/apps/meteor/app/2fa/server/code/EmailCheck.ts +++ b/apps/meteor/app/2fa/server/code/EmailCheck.ts @@ -1,13 +1,13 @@ -import { Random } from '@rocket.chat/random'; -import { Accounts } from 'meteor/accounts-base'; -import bcrypt from 'bcrypt'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import bcrypt from 'bcrypt'; +import { Accounts } from 'meteor/accounts-base'; -import { settings } from '../../../settings/server'; +import { i18n } from '../../../../server/lib/i18n'; import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; import type { ICodeCheck, IProcessInvalidCodeResult } from './ICodeCheck'; -import { i18n } from '../../../../server/lib/i18n'; export class EmailCheck implements ICodeCheck { public readonly name = 'email'; diff --git a/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts b/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts index fc054130049b..10d2f01fadbb 100644 --- a/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts +++ b/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts @@ -1,5 +1,5 @@ -import { Accounts } from 'meteor/accounts-base'; import type { IUser } from '@rocket.chat/core-typings'; +import { Accounts } from 'meteor/accounts-base'; import { settings } from '../../../settings/server'; import type { ICodeCheck, IProcessInvalidCodeResult } from './ICodeCheck'; diff --git a/apps/meteor/app/2fa/server/code/TOTPCheck.ts b/apps/meteor/app/2fa/server/code/TOTPCheck.ts index ecf5db9e1db9..2ed91d7ec2aa 100644 --- a/apps/meteor/app/2fa/server/code/TOTPCheck.ts +++ b/apps/meteor/app/2fa/server/code/TOTPCheck.ts @@ -1,7 +1,7 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { TOTP } from '../lib/totp'; import { settings } from '../../../settings/server'; +import { TOTP } from '../lib/totp'; import type { ICodeCheck, IProcessInvalidCodeResult } from './ICodeCheck'; export class TOTPCheck implements ICodeCheck { diff --git a/apps/meteor/app/2fa/server/code/index.ts b/apps/meteor/app/2fa/server/code/index.ts index 5a365a4fc480..250fd5b158ca 100644 --- a/apps/meteor/app/2fa/server/code/index.ts +++ b/apps/meteor/app/2fa/server/code/index.ts @@ -1,15 +1,15 @@ import crypto from 'crypto'; -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; import type { IUser, IMethodConnection } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; -import { TOTPCheck } from './TOTPCheck'; import { EmailCheck } from './EmailCheck'; -import { PasswordCheckFallback } from './PasswordCheckFallback'; import type { ICodeCheck } from './ICodeCheck'; +import { PasswordCheckFallback } from './PasswordCheckFallback'; +import { TOTPCheck } from './TOTPCheck'; export interface ITwoFactorOptions { disablePasswordFallback?: boolean; diff --git a/apps/meteor/app/2fa/server/functions/resetTOTP.ts b/apps/meteor/app/2fa/server/functions/resetTOTP.ts index 5374abb88b43..85fe696babe1 100644 --- a/apps/meteor/app/2fa/server/functions/resetTOTP.ts +++ b/apps/meteor/app/2fa/server/functions/resetTOTP.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; -import * as Mailer from '../../../mailer/server/api'; -import { isUserIdFederated } from '../../../../server/lib/isUserIdFederated'; import { i18n } from '../../../../server/lib/i18n'; +import { isUserIdFederated } from '../../../../server/lib/isUserIdFederated'; +import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; const sendResetNotification = async function (uid: string): Promise { const user = await Users.findOneById>(uid, { diff --git a/apps/meteor/app/2fa/server/lib/totp.ts b/apps/meteor/app/2fa/server/lib/totp.ts index d1855f21cb8e..87a4c39ac251 100644 --- a/apps/meteor/app/2fa/server/lib/totp.ts +++ b/apps/meteor/app/2fa/server/lib/totp.ts @@ -1,7 +1,7 @@ -import { SHA256 } from '@rocket.chat/sha256'; +import { Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; +import { SHA256 } from '@rocket.chat/sha256'; import speakeasy from 'speakeasy'; -import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/2fa/server/loginHandler.ts b/apps/meteor/app/2fa/server/loginHandler.ts index a165359263e6..feb1923c8799 100644 --- a/apps/meteor/app/2fa/server/loginHandler.ts +++ b/apps/meteor/app/2fa/server/loginHandler.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; -import { OAuth } from 'meteor/oauth'; import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { OAuth } from 'meteor/oauth'; import { callbacks } from '../../../lib/callbacks'; import { checkCodeForUser } from './code/index'; diff --git a/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts b/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts index 3ea3ef25fc73..219a78483cea 100644 --- a/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts +++ b/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/2fa/server/methods/disable.ts b/apps/meteor/app/2fa/server/methods/disable.ts index ad1adf21b503..8768f86c0424 100644 --- a/apps/meteor/app/2fa/server/methods/disable.ts +++ b/apps/meteor/app/2fa/server/methods/disable.ts @@ -1,6 +1,6 @@ +import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Users } from '@rocket.chat/models'; import { TOTP } from '../lib/totp'; diff --git a/apps/meteor/app/2fa/server/methods/enable.ts b/apps/meteor/app/2fa/server/methods/enable.ts index 8d68fb89dc1b..3b9f35dfcd9d 100644 --- a/apps/meteor/app/2fa/server/methods/enable.ts +++ b/apps/meteor/app/2fa/server/methods/enable.ts @@ -1,6 +1,6 @@ +import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Users } from '@rocket.chat/models'; import { TOTP } from '../lib/totp'; diff --git a/apps/meteor/app/2fa/server/methods/regenerateCodes.ts b/apps/meteor/app/2fa/server/methods/regenerateCodes.ts index 6bfe95b61077..e0f4cfaa4797 100644 --- a/apps/meteor/app/2fa/server/methods/regenerateCodes.ts +++ b/apps/meteor/app/2fa/server/methods/regenerateCodes.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { TOTP } from '../lib/totp'; diff --git a/apps/meteor/app/2fa/server/methods/validateTempToken.ts b/apps/meteor/app/2fa/server/methods/validateTempToken.ts index 71d90abe9dcb..5931d0a8e80d 100644 --- a/apps/meteor/app/2fa/server/methods/validateTempToken.ts +++ b/apps/meteor/app/2fa/server/methods/validateTempToken.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { TOTP } from '../lib/totp'; diff --git a/apps/meteor/app/analytics/client/loadScript.ts b/apps/meteor/app/analytics/client/loadScript.ts index 47d4a10f764a..32a42cc695c7 100644 --- a/apps/meteor/app/analytics/client/loadScript.ts +++ b/apps/meteor/app/analytics/client/loadScript.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { useEffect } from 'react'; -import { settings } from '../../settings/client'; import { useReactiveValue } from '../../../client/hooks/useReactiveValue'; +import { settings } from '../../settings/client'; declare global { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -36,7 +36,6 @@ export const useAnalytics = (): void => { if (!googleId) { return; } - /*eslint-disable */ if (googleId.startsWith('G-')) { // Google Analytics 4 const f = document.getElementsByTagName('script')[0]; @@ -47,22 +46,24 @@ export const useAnalytics = (): void => { // injecting the dataLayer into the windows global object const w: Window & { dataLayer?: any } = window; - let dataLayer = w.dataLayer || []; - function gtag(key: string, value: any) { + const dataLayer = w.dataLayer || []; + const gtag = (key: string, value: any) => { dataLayer.push(key, value); - } + }; gtag('js', new Date()); gtag('config', googleId); } else { // Google Analytics 3 (function (i, s, o, g, r: 'ga', a?: any, m?: any) { - i['GoogleAnalyticsObject'] = r; + i.GoogleAnalyticsObject = r; (i[r] = i[r] || - function () { - ((i[r] as any).q = (i[r] as any).q || []).push(arguments); + function (...args) { + ((i[r] as any).q = (i[r] as any).q || []).push(args); + // eslint-disable-next-line no-sequences }), ((i[r] as any).l = new Date().getTime()); + // eslint-disable-next-line no-sequences (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]); a.async = 1; a.src = g; @@ -72,7 +73,6 @@ export const useAnalytics = (): void => { window.ga?.('create', googleId, 'auto'); window.ga?.('send', 'pageview'); } - /* eslint-enable */ }, [googleId, uid]); useEffect(() => { diff --git a/apps/meteor/app/api/server/api.ts b/apps/meteor/app/api/server/api.ts index ca416b7c692a..d4e8377a4dd7 100644 --- a/apps/meteor/app/api/server/api.ts +++ b/apps/meteor/app/api/server/api.ts @@ -1,25 +1,28 @@ -import { Meteor } from 'meteor/meteor'; +import type { IMethodConnection, IUser, IRoom } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; +import { Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import { DDPCommon } from 'meteor/ddp-common'; -import { DDP } from 'meteor/ddp'; +import type { JoinPathPattern, Method } from '@rocket.chat/rest-typings'; import { Accounts } from 'meteor/accounts-base'; +import { DDP } from 'meteor/ddp'; +import { DDPCommon } from 'meteor/ddp-common'; +import { Meteor } from 'meteor/meteor'; +import type { RateLimiterOptionsToCheck } from 'meteor/rate-limit'; +import { RateLimiter } from 'meteor/rate-limit'; import type { Request, Response } from 'meteor/rocketchat:restivus'; import { Restivus } from 'meteor/rocketchat:restivus'; import _ from 'underscore'; -import type { RateLimiterOptionsToCheck } from 'meteor/rate-limit'; -import { RateLimiter } from 'meteor/rate-limit'; -import type { IMethodConnection, IUser, IRoom } from '@rocket.chat/core-typings'; -import type { JoinPathPattern, Method } from '@rocket.chat/rest-typings'; -import { Users } from '@rocket.chat/models'; +import { isObject } from '../../../lib/utils/isObject'; import { getRestPayload } from '../../../server/lib/logger/logPayloads'; -import { settings } from '../../settings/server'; +import { checkCodeForUser } from '../../2fa/server/code'; +import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; +import { apiDeprecationLogger } from '../../lib/server/lib/deprecationWarningLogger'; import { metrics } from '../../metrics/server'; +import { settings } from '../../settings/server'; import { getDefaultUserFields } from '../../utils/server/functions/getDefaultUserFields'; -import { checkCodeForUser } from '../../2fa/server/code'; import type { PermissionsPayload } from './api.helpers'; import { checkPermissionsForInvocation, checkPermissions } from './api.helpers'; -import { isObject } from '../../../lib/utils/isObject'; import type { FailureResult, InternalError, @@ -30,11 +33,8 @@ import type { SuccessResult, UnauthorizedResult, } from './definition'; -import { parseJsonQuery } from './helpers/parseJsonQuery'; -import { Logger } from '../../logger/server'; import { getUserInfo } from './helpers/getUserInfo'; -import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; -import { apiDeprecationLogger } from '../../lib/server/lib/deprecationWarningLogger'; +import { parseJsonQuery } from './helpers/parseJsonQuery'; const logger = new Logger('API'); diff --git a/apps/meteor/app/api/server/definition.ts b/apps/meteor/app/api/server/definition.ts index eb9ba1857545..d2fa248530ff 100644 --- a/apps/meteor/app/api/server/definition.ts +++ b/apps/meteor/app/api/server/definition.ts @@ -1,10 +1,10 @@ -import type { Method, MethodOf, OperationParams, OperationResult, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; import type { IUser } from '@rocket.chat/core-typings'; +import type { Logger } from '@rocket.chat/logger'; +import type { Method, MethodOf, OperationParams, OperationResult, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; import type { ValidateFunction } from 'ajv'; import type { Request, Response } from 'express'; import type { ITwoFactorOptions } from '../../2fa/server/code'; -import type { Logger } from '../../logger/server'; export type SuccessResult = { statusCode: 200; diff --git a/apps/meteor/app/api/server/helpers/getLoggedInUser.ts b/apps/meteor/app/api/server/helpers/getLoggedInUser.ts index 18abd1c97240..55c7c2d21955 100644 --- a/apps/meteor/app/api/server/helpers/getLoggedInUser.ts +++ b/apps/meteor/app/api/server/helpers/getLoggedInUser.ts @@ -1,7 +1,7 @@ -import { Accounts } from 'meteor/accounts-base'; -import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import type { Request } from 'express'; +import { Accounts } from 'meteor/accounts-base'; export async function getLoggedInUser(request: Request): Promise | null> { const token = request.headers['x-auth-token']; diff --git a/apps/meteor/app/api/server/helpers/getUserFromParams.ts b/apps/meteor/app/api/server/helpers/getUserFromParams.ts index d2dbe475c028..ad2efce90fe5 100644 --- a/apps/meteor/app/api/server/helpers/getUserFromParams.ts +++ b/apps/meteor/app/api/server/helpers/getUserFromParams.ts @@ -1,7 +1,7 @@ // Convenience method, almost need to turn it into a middleware of sorts -import { Meteor } from 'meteor/meteor'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; export async function getUserFromParams(params: { userId?: string; diff --git a/apps/meteor/app/api/server/helpers/getUserInfo.ts b/apps/meteor/app/api/server/helpers/getUserInfo.ts index 2538c59175fd..9237726366c9 100644 --- a/apps/meteor/app/api/server/helpers/getUserInfo.ts +++ b/apps/meteor/app/api/server/helpers/getUserInfo.ts @@ -1,7 +1,8 @@ import type { IUser, IUserEmail } from '@rocket.chat/core-typings'; import { settings } from '../../../settings/server'; -import { getUserPreference, getURL } from '../../../utils/server'; +import { getURL } from '../../../utils/server/getURL'; +import { getUserPreference } from '../../../utils/server/lib/getUserPreference'; const isVerifiedEmail = (me: IUser): false | IUserEmail | undefined => { if (!me || !Array.isArray(me.emails)) { diff --git a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts index ff04c5bb7cf8..e3552afb5d50 100644 --- a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts +++ b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; import ejson from 'ejson'; +import { Meteor } from 'meteor/meteor'; -import { isValidQuery } from '../lib/isValidQuery'; -import { clean } from '../lib/cleanQuery'; -import { API } from '../api'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { API } from '../api'; import type { PartialThis } from '../definition'; +import { clean } from '../lib/cleanQuery'; +import { isValidQuery } from '../lib/isValidQuery'; const pathAllowConf = { '/api/v1/users.list': ['$or', '$regex', '$and'], diff --git a/apps/meteor/app/api/server/index.ts b/apps/meteor/app/api/server/index.ts index 699bfdacb3aa..00f1a62f1cbd 100644 --- a/apps/meteor/app/api/server/index.ts +++ b/apps/meteor/app/api/server/index.ts @@ -46,6 +46,7 @@ import './v1/voip/extensions'; import './v1/voip/queues'; import './v1/voip/omnichannel'; import './v1/voip'; +import './v1/federation'; import './v1/moderation'; export { API, APIClass, defaultRateLimiterOptions } from './api'; diff --git a/apps/meteor/app/api/server/lib/cleanQuery.ts b/apps/meteor/app/api/server/lib/cleanQuery.ts index 0b4d1d9952ee..ee63fa451569 100644 --- a/apps/meteor/app/api/server/lib/cleanQuery.ts +++ b/apps/meteor/app/api/server/lib/cleanQuery.ts @@ -16,7 +16,6 @@ export const removeDangerousProps = (v: Query): Query => { export function clean(v: Query, allowList: string[] = []): Query { const typedParam = removeDangerousProps(v); if (v instanceof Object) { - /* eslint-disable guard-for-in */ for (const key in typedParam) { if (key.startsWith('$') && !allowList.includes(key)) { delete typedParam[key]; diff --git a/apps/meteor/app/api/server/lib/emailInbox.ts b/apps/meteor/app/api/server/lib/emailInbox.ts index 98b05f14644b..663459c7bc05 100644 --- a/apps/meteor/app/api/server/lib/emailInbox.ts +++ b/apps/meteor/app/api/server/lib/emailInbox.ts @@ -1,6 +1,6 @@ import type { IEmailInbox } from '@rocket.chat/core-typings'; -import type { Filter, InsertOneResult, Sort, UpdateResult, WithId } from 'mongodb'; import { EmailInbox, Users } from '@rocket.chat/models'; +import type { Filter, InsertOneResult, Sort, UpdateResult, WithId } from 'mongodb'; export const findEmailInboxes = async ({ query = {}, diff --git a/apps/meteor/app/api/server/lib/emoji-custom.ts b/apps/meteor/app/api/server/lib/emoji-custom.ts index c94139c82db0..d0b035f3d7e3 100644 --- a/apps/meteor/app/api/server/lib/emoji-custom.ts +++ b/apps/meteor/app/api/server/lib/emoji-custom.ts @@ -1,6 +1,6 @@ import type { IEmojiCustom } from '@rocket.chat/core-typings'; -import type { Filter, FindOptions } from 'mongodb'; import { EmojiCustom } from '@rocket.chat/models'; +import type { Filter, FindOptions } from 'mongodb'; export async function findEmojisCustom({ query = {}, diff --git a/apps/meteor/app/api/server/lib/getServerInfo.ts b/apps/meteor/app/api/server/lib/getServerInfo.ts index a4f9f953b8bd..39f4b82b350b 100644 --- a/apps/meteor/app/api/server/lib/getServerInfo.ts +++ b/apps/meteor/app/api/server/lib/getServerInfo.ts @@ -1,5 +1,5 @@ -import { Info } from '../../../utils/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { Info } from '../../../utils/rocketchat.info'; type ServerInfo = | { diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index 5df41638b90a..9b8f69fb3a66 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -1,9 +1,9 @@ import type { Readable } from 'stream'; -import type { Request } from 'express'; -import busboy from 'busboy'; -import type { ValidateFunction } from 'ajv'; import { MeteorError } from '@rocket.chat/core-services'; +import type { ValidateFunction } from 'ajv'; +import busboy from 'busboy'; +import type { Request } from 'express'; type UploadResult = { file: Readable & { truncated: boolean }; @@ -69,11 +69,11 @@ export async function getUploadFormData< } const fileChunks: Uint8Array[] = []; - file.on('data', function (chunk) { + file.on('data', (chunk) => { fileChunks.push(chunk); }); - file.on('end', function () { + file.on('end', () => { if (file.truncated) { fileChunks.length = 0; return returnError(new MeteorError('error-file-too-large')); @@ -103,17 +103,17 @@ export async function getUploadFormData< bb.on('end', onEnd); bb.on('finish', onEnd); - bb.on('error', function (err: Error) { + bb.on('error', (err: Error) => { returnError(err); }); - bb.on('partsLimit', function () { + bb.on('partsLimit', () => { returnError(); }); - bb.on('filesLimit', function () { + bb.on('filesLimit', () => { returnError('Just 1 file is allowed'); }); - bb.on('fieldsLimit', function () { + bb.on('fieldsLimit', () => { returnError(); }); diff --git a/apps/meteor/app/api/server/lib/messages.ts b/apps/meteor/app/api/server/lib/messages.ts index d939c45a12c8..cce09a1b2741 100644 --- a/apps/meteor/app/api/server/lib/messages.ts +++ b/apps/meteor/app/api/server/lib/messages.ts @@ -1,6 +1,6 @@ -import type { FindOptions } from 'mongodb'; import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { Rooms, Messages, Users } from '@rocket.chat/models'; +import type { FindOptions } from 'mongodb'; import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; diff --git a/apps/meteor/app/api/server/lib/users.ts b/apps/meteor/app/api/server/lib/users.ts index 8fed9e4e2155..990fda0a0209 100644 --- a/apps/meteor/app/api/server/lib/users.ts +++ b/apps/meteor/app/api/server/lib/users.ts @@ -1,8 +1,8 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; import type { IUser } from '@rocket.chat/core-typings'; -import type { Filter } from 'mongodb'; import { Users, Subscriptions } from '@rocket.chat/models'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; import type { Mongo } from 'meteor/mongo'; +import type { Filter } from 'mongodb'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/api/server/middlewares/authentication.ts b/apps/meteor/app/api/server/middlewares/authentication.ts index 5885a72dafc8..0d644b09d02b 100644 --- a/apps/meteor/app/api/server/middlewares/authentication.ts +++ b/apps/meteor/app/api/server/middlewares/authentication.ts @@ -1,6 +1,6 @@ -import type { Request, Response, NextFunction } from 'express'; import { hashLoginToken } from '@rocket.chat/account-utils'; import { Users } from '@rocket.chat/models'; +import type { Request, Response, NextFunction } from 'express'; import { oAuth2ServerAuth } from '../../../oauth2-server-config/server/oauth/oauth2-server'; diff --git a/apps/meteor/app/api/server/v1/assets.ts b/apps/meteor/app/api/server/v1/assets.ts index 614a1ff46a4d..f2f8ca94a989 100644 --- a/apps/meteor/app/api/server/v1/assets.ts +++ b/apps/meteor/app/api/server/v1/assets.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import { isAssetsUnsetAssetProps } from '@rocket.chat/rest-typings'; +import { Meteor } from 'meteor/meteor'; import { RocketChatAssets } from '../../../assets/server'; +import { settings } from '../../../settings/server'; import { API } from '../api'; import { getUploadFormData } from '../lib/getUploadFormData'; -import { settings } from '../../../settings/server'; API.v1.addRoute( 'assets.setAsset', diff --git a/apps/meteor/app/api/server/v1/autotranslate.ts b/apps/meteor/app/api/server/v1/autotranslate.ts index 5b4ee71a97b7..f7f732e08ed0 100644 --- a/apps/meteor/app/api/server/v1/autotranslate.ts +++ b/apps/meteor/app/api/server/v1/autotranslate.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; +import { Messages } from '@rocket.chat/models'; import { isAutotranslateSaveSettingsParamsPOST, isAutotranslateTranslateMessageParamsPOST, isAutotranslateGetSupportedLanguagesParamsGET, } from '@rocket.chat/rest-typings'; -import { Messages } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; import { settings } from '../../../settings/server'; +import { API } from '../api'; API.v1.addRoute( 'autotranslate.getSupportedLanguages', diff --git a/apps/meteor/app/api/server/v1/banners.ts b/apps/meteor/app/api/server/v1/banners.ts index deea03bccca2..4dc74208153b 100644 --- a/apps/meteor/app/api/server/v1/banners.ts +++ b/apps/meteor/app/api/server/v1/banners.ts @@ -1,6 +1,6 @@ -import { Match, check } from 'meteor/check'; -import { BannerPlatform } from '@rocket.chat/core-typings'; import { Banner } from '@rocket.chat/core-services'; +import { BannerPlatform } from '@rocket.chat/core-typings'; +import { Match, check } from 'meteor/check'; import { API } from '../api'; diff --git a/apps/meteor/app/api/server/v1/calendar.ts b/apps/meteor/app/api/server/v1/calendar.ts index 494f5ad03e24..4f189229c322 100644 --- a/apps/meteor/app/api/server/v1/calendar.ts +++ b/apps/meteor/app/api/server/v1/calendar.ts @@ -1,3 +1,4 @@ +import { Calendar } from '@rocket.chat/core-services'; import { isCalendarEventListProps, isCalendarEventCreateProps, @@ -6,7 +7,6 @@ import { isCalendarEventUpdateProps, isCalendarEventDeleteProps, } from '@rocket.chat/rest-typings'; -import { Calendar } from '@rocket.chat/core-services'; import { API } from '../api'; diff --git a/apps/meteor/app/api/server/v1/channels.ts b/apps/meteor/app/api/server/v1/channels.ts index 8b2a6786f5db..70b7fc875082 100644 --- a/apps/meteor/app/api/server/v1/channels.ts +++ b/apps/meteor/app/api/server/v1/channels.ts @@ -1,5 +1,6 @@ -import { Meteor } from 'meteor/meteor'; +import { Team } from '@rocket.chat/core-services'; import type { IRoom, ISubscription, IUser, RoomType } from '@rocket.chat/core-typings'; +import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models'; import { isChannelsAddAllProps, isChannelsArchiveProps, @@ -18,29 +19,28 @@ import { isChannelsSetReadOnlyProps, isChannelsDeleteProps, } from '@rocket.chat/rest-typings'; -import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models'; -import { Team } from '@rocket.chat/core-services'; +import { Meteor } from 'meteor/meteor'; +import { isTruthy } from '../../../../lib/isTruthy'; +import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; +import { hideRoomMethod } from '../../../../server/methods/hideRoom'; +import { removeUserFromRoomMethod } from '../../../../server/methods/removeUserFromRoom'; import { canAccessRoomAsync } from '../../../authorization/server'; import { hasPermissionAsync, hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; +import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; +import { addUsersToRoomMethod } from '../../../lib/server/methods/addUsersToRoom'; +import { createChannelMethod } from '../../../lib/server/methods/createChannel'; +import { joinRoomMethod } from '../../../lib/server/methods/joinRoom'; +import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; +import { settings } from '../../../settings/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; import { addUserToFileObj } from '../helpers/addUserToFileObj'; -import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; -import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; -import { settings } from '../../../settings/server'; import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage'; import { getLoggedInUser } from '../helpers/getLoggedInUser'; import { getPaginationItems } from '../helpers/getPaginationItems'; import { getUserFromParams, getUserListFromParams } from '../helpers/getUserFromParams'; -import { removeUserFromRoomMethod } from '../../../../server/methods/removeUserFromRoom'; -import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; -import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; -import { createChannelMethod } from '../../../lib/server/methods/createChannel'; -import { hideRoomMethod } from '../../../../server/methods/hideRoom'; -import { addUsersToRoomMethod } from '../../../lib/server/methods/addUsersToRoom'; -import { isTruthy } from '../../../../lib/isTruthy'; -import { joinRoomMethod } from '../../../lib/server/methods/joinRoom'; // Returns the channel IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property async function findChannelByIdOrName({ diff --git a/apps/meteor/app/api/server/v1/chat.ts b/apps/meteor/app/api/server/v1/chat.ts index 46ccc45d56a0..75fc98e69c4d 100644 --- a/apps/meteor/app/api/server/v1/chat.ts +++ b/apps/meteor/app/api/server/v1/chat.ts @@ -1,25 +1,26 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { Messages, Users, Rooms, Subscriptions } from '@rocket.chat/models'; -import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Message } from '@rocket.chat/core-services'; import type { IMessage } from '@rocket.chat/core-typings'; +import { Messages, Users, Rooms, Subscriptions } from '@rocket.chat/models'; import { isChatReportMessageProps } from '@rocket.chat/rest-typings'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { reportMessage } from '../../../../server/lib/moderation/reportMessage'; import { roomAccessAttributes } from '../../../authorization/server'; +import { canAccessRoomAsync, canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; +import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; -import { API } from '../api'; +import { deleteMessageValidatingPermission } from '../../../lib/server/functions/deleteMessage'; import { processWebhookMessage } from '../../../lib/server/functions/processWebhookMessage'; -import { settings } from '../../../settings/server'; -import { executeSetReaction } from '../../../reactions/server/setReaction'; -import { findDiscussionsFromRoom, findMentionedMessages, findStarredMessages } from '../lib/messages'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; +import { executeUpdateMessage } from '../../../lib/server/methods/updateMessage'; +import { executeSetReaction } from '../../../reactions/server/setReaction'; +import { settings } from '../../../settings/server'; +import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; -import { canAccessRoomAsync, canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; -import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; -import { deleteMessageValidatingPermission } from '../../../lib/server/functions/deleteMessage'; -import { reportMessage } from '../../../../server/lib/moderation/reportMessage'; +import { findDiscussionsFromRoom, findMentionedMessages, findStarredMessages } from '../lib/messages'; API.v1.addRoute( 'chat.delete', @@ -215,7 +216,7 @@ API.v1.addRoute( throw new Meteor.Error('error-invalid-params', 'The "message" parameter must be provided.'); } - const sent = await executeSendMessage(this.userId, this.bodyParams.message as Pick); + const sent = await executeSendMessage(this.userId, this.bodyParams.message as Pick, this.bodyParams.previewUrls); const [message] = await normalizeMessagesForUser([sent], this.userId); return API.v1.success({ @@ -310,6 +311,7 @@ API.v1.addRoute( roomId: String, msgId: String, text: String, // Using text to be consistant with chat.postMessage + previewUrls: Match.Maybe([String]), }), ); @@ -325,7 +327,7 @@ API.v1.addRoute( } // Permission checks are already done in the updateMessage method, so no need to duplicate them - await Meteor.callAsync('updateMessage', { _id: msg._id, msg: this.bodyParams.text, rid: msg.rid }); + await executeUpdateMessage(this.userId, { _id: msg._id, msg: this.bodyParams.text, rid: msg.rid }, this.bodyParams.previewUrls); const updatedMessage = await Messages.findOneById(msg._id); const [message] = await normalizeMessagesForUser(updatedMessage ? [updatedMessage] : [], this.userId); diff --git a/apps/meteor/app/api/server/v1/cloud.ts b/apps/meteor/app/api/server/v1/cloud.ts index fabdbb2be59c..55b3f8588275 100644 --- a/apps/meteor/app/api/server/v1/cloud.ts +++ b/apps/meteor/app/api/server/v1/cloud.ts @@ -1,13 +1,13 @@ import { check } from 'meteor/check'; -import { API } from '../api'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; -import { saveRegistrationData } from '../../../cloud/server/functions/saveRegistrationData'; +import { getConfirmationPoll } from '../../../cloud/server/functions/getConfirmationPoll'; +import { registerPreIntentWorkspaceWizard } from '../../../cloud/server/functions/registerPreIntentWorkspaceWizard'; import { retrieveRegistrationStatus } from '../../../cloud/server/functions/retrieveRegistrationStatus'; +import { saveRegistrationData } from '../../../cloud/server/functions/saveRegistrationData'; import { startRegisterWorkspaceSetupWizard } from '../../../cloud/server/functions/startRegisterWorkspaceSetupWizard'; -import { registerPreIntentWorkspaceWizard } from '../../../cloud/server/functions/registerPreIntentWorkspaceWizard'; -import { getConfirmationPoll } from '../../../cloud/server/functions/getConfirmationPoll'; +import { API } from '../api'; API.v1.addRoute( 'cloud.manualRegister', diff --git a/apps/meteor/app/api/server/v1/commands.ts b/apps/meteor/app/api/server/v1/commands.ts index 8e3e7ceb3edd..c4719386864b 100644 --- a/apps/meteor/app/api/server/v1/commands.ts +++ b/apps/meteor/app/api/server/v1/commands.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; +import { Messages } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; +import { Meteor } from 'meteor/meteor'; import objectPath from 'object-path'; -import { Messages } from '@rocket.chat/models'; -import { slashCommands } from '../../../utils/server'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; +import { slashCommands } from '../../../utils/server/slashCommand'; import { API } from '../api'; import { getLoggedInUser } from '../helpers/getLoggedInUser'; import { getPaginationItems } from '../helpers/getPaginationItems'; diff --git a/apps/meteor/app/api/server/v1/custom-user-status.ts b/apps/meteor/app/api/server/v1/custom-user-status.ts index 04ac1edc2178..d665313b98e6 100644 --- a/apps/meteor/app/api/server/v1/custom-user-status.ts +++ b/apps/meteor/app/api/server/v1/custom-user-status.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { CustomUserStatus } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; diff --git a/apps/meteor/app/api/server/v1/dns.ts b/apps/meteor/app/api/server/v1/dns.ts index 96231b4b43a2..cd1e20fbe4ad 100644 --- a/apps/meteor/app/api/server/v1/dns.ts +++ b/apps/meteor/app/api/server/v1/dns.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; import { resolveSRV, resolveTXT } from '../../../federation/server/functions/resolveDNS'; +import { API } from '../api'; /** * @openapi diff --git a/apps/meteor/app/api/server/v1/e2e.ts b/apps/meteor/app/api/server/v1/e2e.ts index 513a5b1b81fd..8cb3e8ab4236 100644 --- a/apps/meteor/app/api/server/v1/e2e.ts +++ b/apps/meteor/app/api/server/v1/e2e.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; +import type { IUser } from '@rocket.chat/core-typings'; import { ise2eGetUsersOfRoomWithoutKeyParamsGET, ise2eSetRoomKeyIDParamsPOST, ise2eSetUserPublicAndPrivateKeysParamsPOST, ise2eUpdateGroupKeyParamsPOST, } from '@rocket.chat/rest-typings'; -import type { IUser } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; import { handleSuggestedGroupKey } from '../../../e2e/server/functions/handleSuggestedGroupKey'; +import { API } from '../api'; API.v1.addRoute( 'e2e.fetchMyKeys', diff --git a/apps/meteor/app/api/server/v1/email-inbox.ts b/apps/meteor/app/api/server/v1/email-inbox.ts index f7e65d046e3c..5748565a0f77 100644 --- a/apps/meteor/app/api/server/v1/email-inbox.ts +++ b/apps/meteor/app/api/server/v1/email-inbox.ts @@ -1,10 +1,10 @@ -import { check, Match } from 'meteor/check'; import { EmailInbox, Users } from '@rocket.chat/models'; +import { check, Match } from 'meteor/check'; -import { API } from '../api'; -import { insertOneEmailInbox, findEmailInboxes, findOneEmailInbox, updateEmailInbox } from '../lib/emailInbox'; import { sendTestEmailToInbox } from '../../../../server/features/EmailInbox/EmailInbox_Outgoing'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; +import { insertOneEmailInbox, findEmailInboxes, findOneEmailInbox, updateEmailInbox } from '../lib/emailInbox'; API.v1.addRoute( 'email-inbox.list', diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index f3136190b894..a61149c5e66e 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { EmojiCustom } from '@rocket.chat/models'; import { Media } from '@rocket.chat/core-services'; +import { EmojiCustom } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; -import { getUploadFormData } from '../lib/getUploadFormData'; -import { findEmojisCustom } from '../lib/emoji-custom'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; +import { findEmojisCustom } from '../lib/emoji-custom'; +import { getUploadFormData } from '../lib/getUploadFormData'; API.v1.addRoute( 'emoji-custom.list', diff --git a/apps/meteor/app/api/server/v1/federation.ts b/apps/meteor/app/api/server/v1/federation.ts new file mode 100644 index 000000000000..02fc30763eeb --- /dev/null +++ b/apps/meteor/app/api/server/v1/federation.ts @@ -0,0 +1,24 @@ +import { Federation, FederationEE } from '@rocket.chat/core-services'; +import { isFederationVerifyMatrixIdProps } from '@rocket.chat/rest-typings'; + +import { isEnterprise } from '../../../../ee/app/license/server'; +import { API } from '../api'; + +API.v1.addRoute( + 'federation/matrixIds.verify', + { + authRequired: true, + validateParams: isFederationVerifyMatrixIdProps, + }, + { + async get() { + const { matrixIds } = this.queryParams; + + const federationService = isEnterprise() ? FederationEE : Federation; + + const results = await federationService.verifyMatrixIds(matrixIds); + + return API.v1.success({ results: Object.fromEntries(results) }); + }, + }, +); diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index e9ae57b5228b..df54b683fda4 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -6,25 +6,25 @@ import { Meteor } from 'meteor/meteor'; import type { Filter } from 'mongodb'; import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; +import { hideRoomMethod } from '../../../../server/methods/hideRoom'; +import { removeUserFromRoomMethod } from '../../../../server/methods/removeUserFromRoom'; import { canAccessRoomAsync, roomAccessAttributes } from '../../../authorization/server'; import { hasAllPermissionAsync, hasAtLeastOnePermissionAsync, hasPermissionAsync, } from '../../../authorization/server/functions/hasPermission'; +import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; +import { createPrivateGroupMethod } from '../../../lib/server/methods/createPrivateGroup'; +import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; import { addUserToFileObj } from '../helpers/addUserToFileObj'; import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage'; import { getLoggedInUser } from '../helpers/getLoggedInUser'; import { getPaginationItems } from '../helpers/getPaginationItems'; -import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; -import { removeUserFromRoomMethod } from '../../../../server/methods/removeUserFromRoom'; -import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; import { getUserFromParams, getUserListFromParams } from '../helpers/getUserFromParams'; -import { createPrivateGroupMethod } from '../../../lib/server/methods/createPrivateGroup'; -import { hideRoomMethod } from '../../../../server/methods/hideRoom'; // Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property async function findPrivateGroupByIdOrName({ diff --git a/apps/meteor/app/api/server/v1/im.ts b/apps/meteor/app/api/server/v1/im.ts index b18c691bc962..a640318a9cd0 100644 --- a/apps/meteor/app/api/server/v1/im.ts +++ b/apps/meteor/app/api/server/v1/im.ts @@ -2,6 +2,7 @@ * Docs: https://github.com/RocketChat/developer-docs/blob/master/reference/api/rest-api/endpoints/team-collaboration-endpoints/im-endpoints */ import type { IMessage, IRoom, ISubscription } from '@rocket.chat/core-typings'; +import { Subscriptions, Uploads, Messages, Rooms, Users } from '@rocket.chat/models'; import { isDmDeleteProps, isDmFileProps, @@ -10,22 +11,21 @@ import { isDmCreateProps, isDmHistoryProps, } from '@rocket.chat/rest-typings'; -import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; -import { Subscriptions, Uploads, Messages, Rooms, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; +import { hideRoomMethod } from '../../../../server/methods/hideRoom'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; +import { getRoomByNameOrIdWithOptionToJoin } from '../../../lib/server/functions/getRoomByNameOrIdWithOptionToJoin'; +import { settings } from '../../../settings/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; -import { getRoomByNameOrIdWithOptionToJoin } from '../../../lib/server/functions/getRoomByNameOrIdWithOptionToJoin'; -import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; import { addUserToFileObj } from '../helpers/addUserToFileObj'; -import { settings } from '../../../settings/server'; import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage'; import { getPaginationItems } from '../helpers/getPaginationItems'; -import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; -import { hideRoomMethod } from '../../../../server/methods/hideRoom'; // TODO: Refact or remove type findDirectMessageRoomProps = @@ -123,18 +123,33 @@ API.v1.addRoute( if (!roomId) { throw new Meteor.Error('error-room-param-not-provided', 'Body param "roomId" is required'); } - const canAccess = await canAccessRoomIdAsync(roomId, this.userId); - if (!canAccess) { - return API.v1.unauthorized(); + + let subscription; + + const roomExists = !!(await Rooms.findOneById(roomId)); + if (!roomExists) { + // even if the room doesn't exist, we should allow the user to close the subscription anyways + subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId); + } else { + const canAccess = await canAccessRoomIdAsync(roomId, this.userId); + if (!canAccess) { + return API.v1.unauthorized(); + } + + const { subscription: subs } = await findDirectMessageRoom({ roomId }, this.userId); + + subscription = subs; } - const { room, subscription } = await findDirectMessageRoom({ roomId }, this.userId); + if (!subscription) { + return API.v1.failure(`The user is not subscribed to the room`); + } - if (!subscription?.open) { + if (!subscription.open) { return API.v1.failure(`The direct message room, is already closed to the sender`); } - await hideRoomMethod(this.userId, room._id); + await hideRoomMethod(this.userId, roomId); return API.v1.success(); }, diff --git a/apps/meteor/app/api/server/v1/import.ts b/apps/meteor/app/api/server/v1/import.ts index f4ceb6bae2dc..d0fc8643e07c 100644 --- a/apps/meteor/app/api/server/v1/import.ts +++ b/apps/meteor/app/api/server/v1/import.ts @@ -1,4 +1,5 @@ -import { Meteor } from 'meteor/meteor'; +import { Import } from '@rocket.chat/core-services'; +import { Imports } from '@rocket.chat/models'; import { isUploadImportFileParamsPOST, isDownloadPublicImportFileParamsPOST, @@ -11,10 +12,8 @@ import { isGetCurrentImportOperationParamsGET, isImportAddUsersParamsPOST, } from '@rocket.chat/rest-typings'; -import { Imports } from '@rocket.chat/models'; -import { Import } from '@rocket.chat/core-services'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; import { Importers } from '../../../importer/server'; import { executeUploadImportFile, @@ -24,6 +23,7 @@ import { executeStartImport, executeGetLatestImportOperations, } from '../../../importer/server/methods'; +import { API } from '../api'; API.v1.addRoute( 'uploadImportFile', diff --git a/apps/meteor/app/api/server/v1/instances.ts b/apps/meteor/app/api/server/v1/instances.ts index aab0fdb396ae..7b3482e0a7b8 100644 --- a/apps/meteor/app/api/server/v1/instances.ts +++ b/apps/meteor/app/api/server/v1/instances.ts @@ -1,9 +1,9 @@ import { InstanceStatus } from '@rocket.chat/models'; import { Instance as InstanceService } from '../../../../ee/server/sdk'; +import { isRunningMs } from '../../../../server/lib/isRunningMs'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { API } from '../api'; -import { isRunningMs } from '../../../../server/lib/isRunningMs'; const getMatrixInstances = (() => { if (isRunningMs()) { diff --git a/apps/meteor/app/api/server/v1/integrations.ts b/apps/meteor/app/api/server/v1/integrations.ts index 2e680600989f..f7eb9e5a1467 100644 --- a/apps/meteor/app/api/server/v1/integrations.ts +++ b/apps/meteor/app/api/server/v1/integrations.ts @@ -1,6 +1,5 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import type { IIntegration, INewIncomingIntegration, INewOutgoingIntegration } from '@rocket.chat/core-typings'; +import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import { isIntegrationsCreateProps, isIntegrationsHistoryProps, @@ -8,21 +7,22 @@ import { isIntegrationsGetProps, isIntegrationsUpdateProps, } from '@rocket.chat/rest-typings'; -import { Integrations, IntegrationHistory } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { Filter } from 'mongodb'; import { hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { API } from '../api'; import { mountIntegrationHistoryQueryBasedOnPermissions, mountIntegrationQueryBasedOnPermissions, } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; -import { findOneIntegration } from '../lib/integrations'; -import { getPaginationItems } from '../helpers/getPaginationItems'; -import { deleteOutgoingIntegration } from '../../../integrations/server/methods/outgoing/deleteOutgoingIntegration'; +import { addIncomingIntegration } from '../../../integrations/server/methods/incoming/addIncomingIntegration'; import { deleteIncomingIntegration } from '../../../integrations/server/methods/incoming/deleteIncomingIntegration'; import { addOutgoingIntegration } from '../../../integrations/server/methods/outgoing/addOutgoingIntegration'; -import { addIncomingIntegration } from '../../../integrations/server/methods/incoming/addIncomingIntegration'; +import { deleteOutgoingIntegration } from '../../../integrations/server/methods/outgoing/deleteOutgoingIntegration'; +import { API } from '../api'; +import { getPaginationItems } from '../helpers/getPaginationItems'; +import { findOneIntegration } from '../lib/integrations'; API.v1.addRoute( 'integrations.create', diff --git a/apps/meteor/app/api/server/v1/invites.ts b/apps/meteor/app/api/server/v1/invites.ts index 5f4f484075cd..ac053f21f288 100644 --- a/apps/meteor/app/api/server/v1/invites.ts +++ b/apps/meteor/app/api/server/v1/invites.ts @@ -1,4 +1,3 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import type { IInvite } from '@rocket.chat/core-typings'; import { isFindOrCreateInviteParams, @@ -7,13 +6,13 @@ import { isSendInvitationEmailParams, } from '@rocket.chat/rest-typings'; -import { API } from '../api'; import { findOrCreateInvite } from '../../../invites/server/functions/findOrCreateInvite'; -import { removeInvite } from '../../../invites/server/functions/removeInvite'; import { listInvites } from '../../../invites/server/functions/listInvites'; +import { removeInvite } from '../../../invites/server/functions/removeInvite'; +import { sendInvitationEmail } from '../../../invites/server/functions/sendInvitationEmail'; import { useInviteToken } from '../../../invites/server/functions/useInviteToken'; import { validateInviteToken } from '../../../invites/server/functions/validateInviteToken'; -import { sendInvitationEmail } from '../../../invites/server/functions/sendInvitationEmail'; +import { API } from '../api'; API.v1.addRoute( 'listInvites', @@ -66,6 +65,7 @@ API.v1.addRoute( const { token } = this.bodyParams; // eslint-disable-next-line react-hooks/rules-of-hooks + // eslint-disable-next-line react-hooks/rules-of-hooks return API.v1.success(await useInviteToken(this.userId, token)); }, }, diff --git a/apps/meteor/app/api/server/v1/ldap.ts b/apps/meteor/app/api/server/v1/ldap.ts index a841e5625b0f..f0d3a52b504f 100644 --- a/apps/meteor/app/api/server/v1/ldap.ts +++ b/apps/meteor/app/api/server/v1/ldap.ts @@ -1,10 +1,10 @@ -import { Match, check } from 'meteor/check'; import { LDAP } from '@rocket.chat/core-services'; +import { Match, check } from 'meteor/check'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; import { API } from '../api'; -import { SystemLogger } from '../../../../server/lib/logger/system'; API.v1.addRoute( 'ldap.testConnection', diff --git a/apps/meteor/app/api/server/v1/mailer.ts b/apps/meteor/app/api/server/v1/mailer.ts index 136c5c872dec..767868090b91 100644 --- a/apps/meteor/app/api/server/v1/mailer.ts +++ b/apps/meteor/app/api/server/v1/mailer.ts @@ -1,7 +1,7 @@ import { isMailerProps, isMailerUnsubscribeProps } from '@rocket.chat/rest-typings'; -import { API } from '../api'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { API } from '../api'; API.v1.addRoute( 'mailer', diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index 5f52304da614..dec4da6bf87b 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -1,10 +1,7 @@ import crypto from 'crypto'; -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import EJSON from 'ejson'; -import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; -import { escapeHTML } from '@rocket.chat/string-helpers'; +import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { isShieldSvgProps, isSpotlightProps, @@ -14,23 +11,26 @@ import { isMeteorCall, validateParamsPwGetPolicyRest, } from '@rocket.chat/rest-typings'; -import type { IUser } from '@rocket.chat/core-typings'; -import { Users } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import EJSON from 'ejson'; +import { check } from 'meteor/check'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { Meteor } from 'meteor/meteor'; +import { i18n } from '../../../../server/lib/i18n'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { getLogs } from '../../../../server/stream/stdout'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { passwordPolicy } from '../../../lib/server'; +import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; -import { API } from '../api'; import { getDefaultUserFields } from '../../../utils/server/functions/getDefaultUserFields'; import { getURL } from '../../../utils/server/getURL'; -import { getLogs } from '../../../../server/stream/stdout'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { passwordPolicy } from '../../../lib/server'; +import { API } from '../api'; import { getLoggedInUser } from '../helpers/getLoggedInUser'; -import { getUserInfo } from '../helpers/getUserInfo'; import { getPaginationItems } from '../helpers/getPaginationItems'; import { getUserFromParams } from '../helpers/getUserFromParams'; -import { i18n } from '../../../../server/lib/i18n'; -import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { getUserInfo } from '../helpers/getUserInfo'; /** * @openapi @@ -185,7 +185,7 @@ API.v1.addRoute( password: { // The password hash shouldn't be leaked but the client may need to know if it exists. exists: Boolean(services?.password?.bcrypt), - } as any, + }, }, }), }), @@ -232,7 +232,7 @@ API.v1.addRoute( }); } const hideIcon = icon === 'false'; - if (hideIcon && (!name || !name.trim())) { + if (hideIcon && !name?.trim()) { return API.v1.failure('Name cannot be empty when icon is hidden'); } diff --git a/apps/meteor/app/api/server/v1/moderation.ts b/apps/meteor/app/api/server/v1/moderation.ts index 0008a77c5921..fe31487bdc47 100644 --- a/apps/meteor/app/api/server/v1/moderation.ts +++ b/apps/meteor/app/api/server/v1/moderation.ts @@ -1,17 +1,18 @@ +import type { IModerationReport, IUser } from '@rocket.chat/core-typings'; +import { ModerationReports, Users } from '@rocket.chat/models'; import { isReportHistoryProps, isArchiveReportProps, isReportInfoParams, isReportMessageHistoryParams, + isModerationReportUserPost, isModerationDeleteMsgHistoryParams, isReportsByMsgIdParams, } from '@rocket.chat/rest-typings'; -import { ModerationReports, Users } from '@rocket.chat/models'; -import type { IModerationReport, IUser } from '@rocket.chat/core-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { API } from '../api'; import { deleteReportedMessages } from '../../../../server/lib/moderation/deleteReportedMessages'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; type ReportMessage = Pick; @@ -35,7 +36,11 @@ API.v1.addRoute( const escapedSelector = escapeRegExp(selector); - const reports = await ModerationReports.findReportsGroupedByUser(latest, oldest, escapedSelector, { offset, count, sort }).toArray(); + const reports = await ModerationReports.findMessageReportsGroupedByUser(latest, oldest, escapedSelector, { + offset, + count, + sort, + }).toArray(); if (reports.length === 0) { return API.v1.success({ @@ -46,7 +51,7 @@ API.v1.addRoute( }); } - const total = await ModerationReports.countReportsInRange(latest, oldest, escapedSelector); + const total = await ModerationReports.countMessageReportsInRange(latest, oldest, escapedSelector); return API.v1.success({ reports, @@ -143,7 +148,7 @@ API.v1.addRoute( moderator, ); - await ModerationReports.hideReportsByUserId(userId, this.userId, sanitizedReason, 'DELETE Messages'); + await ModerationReports.hideMessageReportsByUserId(userId, this.userId, sanitizedReason, 'DELETE Messages'); return API.v1.success(); }, @@ -181,9 +186,9 @@ API.v1.addRoute( const { userId: moderatorId } = this; if (userId) { - await ModerationReports.hideReportsByUserId(userId, moderatorId, sanitizedReason, action); + await ModerationReports.hideMessageReportsByUserId(userId, moderatorId, sanitizedReason, action); } else { - await ModerationReports.hideReportsByMessageId(msgId as string, moderatorId, sanitizedReason, action); + await ModerationReports.hideMessageReportsByMessageId(msgId as string, moderatorId, sanitizedReason, action); } return API.v1.success(); @@ -243,3 +248,30 @@ API.v1.addRoute( }, }, ); + +API.v1.addRoute( + 'moderation.reportUser', + { + authRequired: true, + validateParams: isModerationReportUserPost, + }, + { + async post() { + const { userId, description } = this.bodyParams; + + const { + user: { _id, name, username, createdAt }, + } = this; + + const reportedUser = await Users.findOneById(userId, { projection: { _id: 1, name: 1, username: 1, emails: 1, createdAt: 1 } }); + + if (!reportedUser) { + return API.v1.failure('Invalid user id provided.'); + } + + await ModerationReports.createWithDescriptionAndUser(reportedUser, description, { _id, name, username, createdAt }); + + return API.v1.success(); + }, + }, +); diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index fdaca860c4f8..ca6a2c3a56be 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -1,10 +1,10 @@ -import { isUpdateOAuthAppParams, isOauthAppsGetParams, isOauthAppsAddParams, isDeleteOAuthAppParams } from '@rocket.chat/rest-typings'; import { OAuthApps } from '@rocket.chat/models'; +import { isUpdateOAuthAppParams, isOauthAppsGetParams, isOauthAppsAddParams, isDeleteOAuthAppParams } from '@rocket.chat/rest-typings'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { API } from '../api'; -import { addOAuthApp } from '../../../oauth2-server-config/server/admin/functions/addOAuthApp'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { addOAuthApp } from '../../../oauth2-server-config/server/admin/functions/addOAuthApp'; +import { API } from '../api'; API.v1.addRoute( 'oauth-apps.list', diff --git a/apps/meteor/app/api/server/v1/permissions.ts b/apps/meteor/app/api/server/v1/permissions.ts index 2a0b9c6c9f02..4b860d6e1eac 100644 --- a/apps/meteor/app/api/server/v1/permissions.ts +++ b/apps/meteor/app/api/server/v1/permissions.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import type { IPermission } from '@rocket.chat/core-typings'; -import { isBodyParamsValidPermissionUpdate } from '@rocket.chat/rest-typings'; import { Permissions, Roles } from '@rocket.chat/models'; +import { isBodyParamsValidPermissionUpdate } from '@rocket.chat/rest-typings'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { API } from '../api'; diff --git a/apps/meteor/app/api/server/v1/push.ts b/apps/meteor/app/api/server/v1/push.ts index 2f36be196410..b5fd93f94dda 100644 --- a/apps/meteor/app/api/server/v1/push.ts +++ b/apps/meteor/app/api/server/v1/push.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; +import { Messages, AppsTokens, Users, Rooms } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; import { Match, check } from 'meteor/check'; -import { Messages, AppsTokens, Users, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; -import PushNotification from '../../../push-notifications/server/lib/PushNotification'; import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; +import PushNotification from '../../../push-notifications/server/lib/PushNotification'; +import { API } from '../api'; API.v1.addRoute( 'push.token', diff --git a/apps/meteor/app/api/server/v1/roles.ts b/apps/meteor/app/api/server/v1/roles.ts index 720b6a7919e8..a0fff9683b80 100644 --- a/apps/meteor/app/api/server/v1/roles.ts +++ b/apps/meteor/app/api/server/v1/roles.ts @@ -1,18 +1,18 @@ -import { Meteor } from 'meteor/meteor'; -import { check, Match } from 'meteor/check'; -import { isRoleAddUserToRoleProps, isRoleDeleteProps, isRoleRemoveUserFromRoleProps } from '@rocket.chat/rest-typings'; +import { api } from '@rocket.chat/core-services'; import type { IRole } from '@rocket.chat/core-typings'; import { Roles, Users } from '@rocket.chat/models'; -import { api } from '@rocket.chat/core-services'; +import { isRoleAddUserToRoleProps, isRoleDeleteProps, isRoleRemoveUserFromRoleProps } from '@rocket.chat/rest-typings'; +import { check, Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; -import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole'; import { getUsersInRolePaginated } from '../../../authorization/server/functions/getUsersInRole'; -import { settings } from '../../../settings/server/index'; -import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { getUserFromParams } from '../helpers/getUserFromParams'; +import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole'; +import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { settings } from '../../../settings/server/index'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; +import { getUserFromParams } from '../helpers/getUserFromParams'; API.v1.addRoute( 'roles.list', diff --git a/apps/meteor/app/api/server/v1/rooms.ts b/apps/meteor/app/api/server/v1/rooms.ts index f277ecb0f9d7..ae08dae04938 100644 --- a/apps/meteor/app/api/server/v1/rooms.ts +++ b/apps/meteor/app/api/server/v1/rooms.ts @@ -1,17 +1,25 @@ -import { Meteor } from 'meteor/meteor'; +import { Media } from '@rocket.chat/core-services'; +import type { IRoom } from '@rocket.chat/core-typings'; +import { Messages, Rooms, Users } from '@rocket.chat/models'; import type { Notifications } from '@rocket.chat/rest-typings'; import { isGETRoomsNameExists } from '@rocket.chat/rest-typings'; -import { Messages, Rooms, Users } from '@rocket.chat/models'; -import type { IRoom } from '@rocket.chat/core-typings'; -import { Media } from '@rocket.chat/core-services'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; +import { isTruthy } from '../../../../lib/isTruthy'; +import * as dataExport from '../../../../server/lib/dataExport'; +import { eraseRoom } from '../../../../server/methods/eraseRoom'; import { canAccessRoomAsync, canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { getUploadFormData } from '../lib/getUploadFormData'; -import { settings } from '../../../settings/server'; -import { eraseRoom } from '../../../../server/methods/eraseRoom'; +import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; +import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; import { FileUpload } from '../../../file-upload/server'; +import { sendFileMessage } from '../../../file-upload/server/methods/sendFileMessage'; +import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; +import { settings } from '../../../settings/server'; +import { API } from '../api'; +import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage'; +import { getPaginationItems } from '../helpers/getPaginationItems'; +import { getUploadFormData } from '../lib/getUploadFormData'; import { findAdminRoom, findAdminRooms, @@ -20,14 +28,6 @@ import { findChannelAndPrivateAutocompleteWithPagination, findRoomsAvailableForTeams, } from '../lib/rooms'; -import * as dataExport from '../../../../server/lib/dataExport'; -import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage'; -import { getPaginationItems } from '../helpers/getPaginationItems'; -import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom'; -import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; -import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; -import { isTruthy } from '../../../../lib/isTruthy'; -import { sendFileMessage } from '../../../file-upload/server/methods/sendFileMessage'; async function findRoomByIdOrName({ params, diff --git a/apps/meteor/app/api/server/v1/settings.ts b/apps/meteor/app/api/server/v1/settings.ts index d5fb4b3d1edc..cbaff50729ff 100644 --- a/apps/meteor/app/api/server/v1/settings.ts +++ b/apps/meteor/app/api/server/v1/settings.ts @@ -1,22 +1,22 @@ -import { Meteor } from 'meteor/meteor'; -import { ServiceConfiguration } from 'meteor/service-configuration'; -import _ from 'underscore'; import type { ISetting, ISettingColor } from '@rocket.chat/core-typings'; import { isSettingAction, isSettingColor } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import { isOauthCustomConfiguration, isSettingsUpdatePropDefault, isSettingsUpdatePropsActions, isSettingsUpdatePropsColor, } from '@rocket.chat/rest-typings'; -import { Settings } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import { ServiceConfiguration } from 'meteor/service-configuration'; import type { FindOptions } from 'mongodb'; +import _ from 'underscore'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import type { ResultFor } from '../definition'; -import { API } from '../api'; import { SettingsEvents, settings } from '../../../settings/server'; import { setValue } from '../../../settings/server/raw'; +import { API } from '../api'; +import type { ResultFor } from '../definition'; import { getPaginationItems } from '../helpers/getPaginationItems'; async function fetchSettings( diff --git a/apps/meteor/app/api/server/v1/stats.ts b/apps/meteor/app/api/server/v1/stats.ts index 357214669886..27cea2c31057 100644 --- a/apps/meteor/app/api/server/v1/stats.ts +++ b/apps/meteor/app/api/server/v1/stats.ts @@ -1,6 +1,6 @@ -import { API } from '../api'; import { getStatistics, getLastStatistics } from '../../../statistics/server'; import telemetryEvent from '../../../statistics/server/lib/telemetryEvents'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; API.v1.addRoute( diff --git a/apps/meteor/app/api/server/v1/subscriptions.ts b/apps/meteor/app/api/server/v1/subscriptions.ts index d1687e4eac84..9d81fe6bef65 100644 --- a/apps/meteor/app/api/server/v1/subscriptions.ts +++ b/apps/meteor/app/api/server/v1/subscriptions.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; +import { Subscriptions } from '@rocket.chat/models'; import { isSubscriptionsGetProps, isSubscriptionsGetOneProps, isSubscriptionsReadProps, isSubscriptionsUnreadProps, } from '@rocket.chat/rest-typings'; -import { Subscriptions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { API } from '../api'; import { readMessages } from '../../../../server/lib/readMessages'; +import { API } from '../api'; API.v1.addRoute( 'subscriptions.get', diff --git a/apps/meteor/app/api/server/v1/teams.ts b/apps/meteor/app/api/server/v1/teams.ts index ca2612f3f7f5..4ea8f2a48f38 100644 --- a/apps/meteor/app/api/server/v1/teams.ts +++ b/apps/meteor/app/api/server/v1/teams.ts @@ -1,6 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Team } from '@rocket.chat/core-services'; +import type { ITeam, UserStatus } from '@rocket.chat/core-typings'; +import { TEAM_TYPE } from '@rocket.chat/core-typings'; +import { Users, Rooms } from '@rocket.chat/models'; import { isTeamsConvertToChannelProps, isTeamsRemoveRoomProps, @@ -11,14 +12,13 @@ import { isTeamsLeaveProps, isTeamsUpdateProps, } from '@rocket.chat/rest-typings'; -import type { ITeam, UserStatus } from '@rocket.chat/core-typings'; -import { TEAM_TYPE } from '@rocket.chat/core-typings'; -import { Team } from '@rocket.chat/core-services'; -import { Users, Rooms } from '@rocket.chat/models'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { removeUserFromRoom } from '../../../lib/server/functions/removeUserFromRoom'; import { canAccessRoomAsync } from '../../../authorization/server'; import { hasPermissionAsync, hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { removeUserFromRoom } from '../../../lib/server/functions/removeUserFromRoom'; import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index 36f5cb30973e..b23d41255c3b 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -1,3 +1,6 @@ +import { Team, api } from '@rocket.chat/core-services'; +import type { IExportOperation, ILoginToken, IPersonalAccessToken, IUser, UserStatus } from '@rocket.chat/core-typings'; +import { Users, Subscriptions } from '@rocket.chat/models'; import { isUserCreateParamsPOST, isUserSetActiveStatusParamsPOST, @@ -14,37 +17,38 @@ import { isUsersCheckUsernameAvailabilityParamsGET, isUsersSendConfirmationEmailParamsPOST, } from '@rocket.chat/rest-typings'; -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { Match, check } from 'meteor/check'; -import type { IExportOperation, ILoginToken, IPersonalAccessToken, IUser, UserStatus } from '@rocket.chat/core-typings'; -import { Users, Subscriptions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import type { Filter } from 'mongodb'; -import { Team, api } from '@rocket.chat/core-services'; +import { i18n } from '../../../../server/lib/i18n'; +import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKey'; +import { saveUserPreferences } from '../../../../server/methods/saveUserPreferences'; +import { getUserForCheck, emailCheck } from '../../../2fa/server/code'; +import { resetTOTP } from '../../../2fa/server/functions/resetTOTP'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { settings } from '../../../settings/server'; -import { validateCustomFields, saveUser, saveCustomFieldsWithoutValidation, setUserAvatar, saveCustomFields } from '../../../lib/server'; import { checkUsernameAvailability, checkUsernameAvailabilityWithValidation, } from '../../../lib/server/functions/checkUsernameAvailability'; import { getFullUserDataByIdOrUsernameOrImportId } from '../../../lib/server/functions/getFullUserData'; +import { saveCustomFields } from '../../../lib/server/functions/saveCustomFields'; +import { saveCustomFieldsWithoutValidation } from '../../../lib/server/functions/saveCustomFieldsWithoutValidation'; +import { saveUser } from '../../../lib/server/functions/saveUser'; import { setStatusText } from '../../../lib/server/functions/setStatusText'; +import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar'; +import { setUsernameWithValidation } from '../../../lib/server/functions/setUsername'; +import { validateCustomFields } from '../../../lib/server/functions/validateCustomFields'; +import { settings } from '../../../settings/server'; +import { getURL } from '../../../utils/server/getURL'; import { API } from '../api'; -import { findUsersToAutocomplete, getInclusiveFields, getNonEmptyFields, getNonEmptyQuery } from '../lib/users'; -import { getUserForCheck, emailCheck } from '../../../2fa/server/code'; -import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKey'; -import { resetTOTP } from '../../../2fa/server/functions/resetTOTP'; -import { isValidQuery } from '../lib/isValidQuery'; -import { getURL } from '../../../utils/server'; -import { getUploadFormData } from '../lib/getUploadFormData'; import { getPaginationItems } from '../helpers/getPaginationItems'; import { getUserFromParams } from '../helpers/getUserFromParams'; import { isUserFromParams } from '../helpers/isUserFromParams'; -import { saveUserPreferences } from '../../../../server/methods/saveUserPreferences'; -import { setUsernameWithValidation } from '../../../lib/server/functions/setUsername'; -import { i18n } from '../../../../server/lib/i18n'; +import { getUploadFormData } from '../lib/getUploadFormData'; +import { isValidQuery } from '../lib/isValidQuery'; +import { findUsersToAutocomplete, getInclusiveFields, getNonEmptyFields, getNonEmptyQuery } from '../lib/users'; API.v1.addRoute( 'users.getAvatar', @@ -122,7 +126,9 @@ API.v1.addRoute( realname: this.bodyParams.data.name, username: this.bodyParams.data.username, nickname: this.bodyParams.data.nickname, + bio: this.bodyParams.data.bio, statusText: this.bodyParams.data.statusText, + statusType: this.bodyParams.data.statusType, newPassword: this.bodyParams.data.newPassword, typedPassword: this.bodyParams.data.currentPassword, }; @@ -1002,10 +1008,6 @@ API.v1.addRoute( throw new Meteor.Error('error-invalid-user-id', 'Invalid user id'); } - if (!settings.get('Accounts_TwoFactorAuthentication_Enforce_Password_Fallback')) { - throw new Meteor.Error('error-not-allowed', 'Not allowed'); - } - if (!(await hasPermissionAsync(this.userId, 'edit-other-user-e2ee'))) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } @@ -1034,8 +1036,8 @@ API.v1.addRoute( throw new Meteor.Error('error-not-allowed', 'Not allowed'); } - if (!settings.get('Accounts_TwoFactorAuthentication_Enforce_Password_Fallback')) { - throw new Meteor.Error('error-not-allowed', 'Not allowed'); + if (!settings.get('Accounts_TwoFactorAuthentication_Enabled')) { + throw new Meteor.Error('error-two-factor-not-enabled', 'Two factor authentication is not enabled'); } const user = await getUserFromParams(this.bodyParams); diff --git a/apps/meteor/app/api/server/v1/videoConference.ts b/apps/meteor/app/api/server/v1/videoConference.ts index 9fb13b7dbdb9..d5e54cbfdf7b 100644 --- a/apps/meteor/app/api/server/v1/videoConference.ts +++ b/apps/meteor/app/api/server/v1/videoConference.ts @@ -1,3 +1,4 @@ +import { VideoConf } from '@rocket.chat/core-services'; import type { VideoConference } from '@rocket.chat/core-typings'; import { isVideoConfStartProps, @@ -6,13 +7,12 @@ import { isVideoConfInfoProps, isVideoConfListProps, } from '@rocket.chat/rest-typings'; -import { VideoConf } from '@rocket.chat/core-services'; -import { API } from '../api'; +import { availabilityErrors } from '../../../../lib/videoConference/constants'; +import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; -import { availabilityErrors } from '../../../../lib/videoConference/constants'; +import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; API.v1.addRoute( diff --git a/apps/meteor/app/api/server/v1/voip/events.ts b/apps/meteor/app/api/server/v1/voip/events.ts index ecda77582cf5..49234480b756 100644 --- a/apps/meteor/app/api/server/v1/voip/events.ts +++ b/apps/meteor/app/api/server/v1/voip/events.ts @@ -1,10 +1,10 @@ -import { Match, check } from 'meteor/check'; +import { LivechatVoip } from '@rocket.chat/core-services'; import { VoipClientEvents } from '@rocket.chat/core-typings'; import { VoipRoom } from '@rocket.chat/models'; -import { LivechatVoip } from '@rocket.chat/core-services'; +import { Match, check } from 'meteor/check'; -import { API } from '../../api'; import { canAccessRoomAsync } from '../../../../authorization/server'; +import { API } from '../../api'; API.v1.addRoute( 'voip/events', diff --git a/apps/meteor/app/api/server/v1/voip/extensions.ts b/apps/meteor/app/api/server/v1/voip/extensions.ts index a27ba1f02b83..e1c2629feb86 100644 --- a/apps/meteor/app/api/server/v1/voip/extensions.ts +++ b/apps/meteor/app/api/server/v1/voip/extensions.ts @@ -1,11 +1,11 @@ -import { Match, check } from 'meteor/check'; +import { Voip } from '@rocket.chat/core-services'; import type { IVoipExtensionBase } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import { Voip } from '@rocket.chat/core-services'; +import { Match, check } from 'meteor/check'; -import { API } from '../../api'; -import { generateJWT } from '../../../../utils/server/lib/JWTHelper'; import { settings } from '../../../../settings/server'; +import { generateJWT } from '../../../../utils/server/lib/JWTHelper'; +import { API } from '../../api'; import { logger } from './logger'; // Get the connector version and type diff --git a/apps/meteor/app/api/server/v1/voip/logger.ts b/apps/meteor/app/api/server/v1/voip/logger.ts index 792cf79427ea..fffa68078480 100644 --- a/apps/meteor/app/api/server/v1/voip/logger.ts +++ b/apps/meteor/app/api/server/v1/voip/logger.ts @@ -1,3 +1,3 @@ -import { Logger } from '../../../../logger/server'; +import { Logger } from '@rocket.chat/logger'; export const logger = new Logger('VoIP'); diff --git a/apps/meteor/app/api/server/v1/voip/omnichannel.ts b/apps/meteor/app/api/server/v1/voip/omnichannel.ts index 7d9915e94bc4..6ffd0005c764 100644 --- a/apps/meteor/app/api/server/v1/voip/omnichannel.ts +++ b/apps/meteor/app/api/server/v1/voip/omnichannel.ts @@ -1,11 +1,11 @@ -import { Match, check } from 'meteor/check'; +import { LivechatVoip } from '@rocket.chat/core-services'; import type { IUser, IVoipExtensionWithAgentInfo } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import { LivechatVoip } from '@rocket.chat/core-services'; +import { Match, check } from 'meteor/check'; import { API } from '../../api'; -import { logger } from './logger'; import { getPaginationItems } from '../../helpers/getPaginationItems'; +import { logger } from './logger'; function filter( array: IVoipExtensionWithAgentInfo[], diff --git a/apps/meteor/app/api/server/v1/voip/queues.ts b/apps/meteor/app/api/server/v1/voip/queues.ts index 02f2e5c439d3..f55dbbadda5f 100644 --- a/apps/meteor/app/api/server/v1/voip/queues.ts +++ b/apps/meteor/app/api/server/v1/voip/queues.ts @@ -1,6 +1,6 @@ -import { Match, check } from 'meteor/check'; -import type { IVoipConnectorResult, IQueueSummary, IQueueMembershipDetails, IQueueMembershipSubscription } from '@rocket.chat/core-typings'; import { Voip } from '@rocket.chat/core-services'; +import type { IVoipConnectorResult, IQueueSummary, IQueueMembershipDetails, IQueueMembershipSubscription } from '@rocket.chat/core-typings'; +import { Match, check } from 'meteor/check'; import { API } from '../../api'; diff --git a/apps/meteor/app/api/server/v1/voip/rooms.ts b/apps/meteor/app/api/server/v1/voip/rooms.ts index df88f87d1feb..402a002dd4e2 100644 --- a/apps/meteor/app/api/server/v1/voip/rooms.ts +++ b/apps/meteor/app/api/server/v1/voip/rooms.ts @@ -1,12 +1,12 @@ -import { Random } from '@rocket.chat/random'; +import { LivechatVoip } from '@rocket.chat/core-services'; import type { ILivechatAgent, IVoipRoom } from '@rocket.chat/core-typings'; -import { isVoipRoomProps, isVoipRoomsProps, isVoipRoomCloseProps } from '@rocket.chat/rest-typings'; import { VoipRoom, LivechatVisitors, Users } from '@rocket.chat/models'; -import { LivechatVoip } from '@rocket.chat/core-services'; +import { Random } from '@rocket.chat/random'; +import { isVoipRoomProps, isVoipRoomsProps, isVoipRoomCloseProps } from '@rocket.chat/rest-typings'; -import { API } from '../../api'; -import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { typedJsonParse } from '../../../../../lib/typedJSONParse'; +import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; +import { API } from '../../api'; import { getPaginationItems } from '../../helpers/getPaginationItems'; type DateParam = { start?: string; end?: string }; diff --git a/apps/meteor/app/api/server/v1/voip/server-connection.ts b/apps/meteor/app/api/server/v1/voip/server-connection.ts index 54eb91422a43..77e6267640f3 100644 --- a/apps/meteor/app/api/server/v1/voip/server-connection.ts +++ b/apps/meteor/app/api/server/v1/voip/server-connection.ts @@ -1,5 +1,5 @@ -import { Match, check } from 'meteor/check'; import { Voip } from '@rocket.chat/core-services'; +import { Match, check } from 'meteor/check'; import { API } from '../../api'; diff --git a/apps/meteor/app/apple/lib/handleIdentityToken.ts b/apps/meteor/app/apple/lib/handleIdentityToken.ts index 886b1cf88591..056777eb1136 100644 --- a/apps/meteor/app/apple/lib/handleIdentityToken.ts +++ b/apps/meteor/app/apple/lib/handleIdentityToken.ts @@ -1,6 +1,6 @@ +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { KJUR } from 'jsrsasign'; import NodeRSA from 'node-rsa'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; async function isValidAppleJWT(identityToken: string, header: any): Promise { const request = await fetch('https://appleid.apple.com/auth/keys', { method: 'GET' }); diff --git a/apps/meteor/app/apple/server/AppleCustomOAuth.ts b/apps/meteor/app/apple/server/AppleCustomOAuth.ts index 39d86743308b..5ec0e8f570bc 100644 --- a/apps/meteor/app/apple/server/AppleCustomOAuth.ts +++ b/apps/meteor/app/apple/server/AppleCustomOAuth.ts @@ -1,5 +1,5 @@ -import { Accounts } from 'meteor/accounts-base'; import { MeteorError } from '@rocket.chat/core-services'; +import { Accounts } from 'meteor/accounts-base'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { handleIdentityToken } from '../lib/handleIdentityToken'; diff --git a/apps/meteor/app/apple/server/loginHandler.ts b/apps/meteor/app/apple/server/loginHandler.ts index 65e4b3aa19f7..18ac7ddd7526 100644 --- a/apps/meteor/app/apple/server/loginHandler.ts +++ b/apps/meteor/app/apple/server/loginHandler.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { handleIdentityToken } from '../lib/handleIdentityToken'; import { settings } from '../../settings/server'; +import { handleIdentityToken } from '../lib/handleIdentityToken'; Accounts.registerLoginHandler('apple', async (loginRequest) => { if (!loginRequest.identityToken) { diff --git a/apps/meteor/app/apps/server/bridges/activation.ts b/apps/meteor/app/apps/server/bridges/activation.ts index 79b9511b80e3..72a5b882b18a 100644 --- a/apps/meteor/app/apps/server/bridges/activation.ts +++ b/apps/meteor/app/apps/server/bridges/activation.ts @@ -1,6 +1,6 @@ -import { AppActivationBridge as ActivationBridge } from '@rocket.chat/apps-engine/server/bridges/AppActivationBridge'; -import type { ProxiedApp } from '@rocket.chat/apps-engine/server/ProxiedApp'; import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; +import type { ProxiedApp } from '@rocket.chat/apps-engine/server/ProxiedApp'; +import { AppActivationBridge as ActivationBridge } from '@rocket.chat/apps-engine/server/bridges/AppActivationBridge'; import { Users } from '@rocket.chat/models'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; diff --git a/apps/meteor/app/apps/server/bridges/api.ts b/apps/meteor/app/apps/server/bridges/api.ts index 485110799cbb..a5767f11b977 100644 --- a/apps/meteor/app/apps/server/bridges/api.ts +++ b/apps/meteor/app/apps/server/bridges/api.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; +import type { RequestMethod } from '@rocket.chat/apps-engine/definition/accessors'; +import type { IApiRequest, IApiEndpoint, IApi } from '@rocket.chat/apps-engine/definition/api'; +import { ApiBridge } from '@rocket.chat/apps-engine/server/bridges/ApiBridge'; +import type { AppApi } from '@rocket.chat/apps-engine/server/managers/AppApi'; import type { Response, Request, IRouter, RequestHandler } from 'express'; import express from 'express'; +import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; -import { ApiBridge } from '@rocket.chat/apps-engine/server/bridges/ApiBridge'; -import type { IApiRequest, IApiEndpoint, IApi } from '@rocket.chat/apps-engine/definition/api'; -import type { AppApi } from '@rocket.chat/apps-engine/server/managers/AppApi'; -import type { RequestMethod } from '@rocket.chat/apps-engine/definition/accessors'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { authenticationMiddleware } from '../../../api/server/middlewares/authentication'; diff --git a/apps/meteor/app/apps/server/bridges/bridges.js b/apps/meteor/app/apps/server/bridges/bridges.js index cefcdd62aded..6d52a1d8e56d 100644 --- a/apps/meteor/app/apps/server/bridges/bridges.js +++ b/apps/meteor/app/apps/server/bridges/bridges.js @@ -1,28 +1,29 @@ import { AppBridges } from '@rocket.chat/apps-engine/server/bridges'; import { AppActivationBridge } from './activation'; -import { AppDetailChangesBridge } from './details'; +import { AppApisBridge } from './api'; import { AppCloudBridge } from './cloud'; import { AppCommandsBridge } from './commands'; -import { AppApisBridge } from './api'; +import { AppDetailChangesBridge } from './details'; import { AppEnvironmentalVariableBridge } from './environmental'; import { AppHttpBridge } from './http'; +import { AppInternalBridge } from './internal'; +import { AppInternalFederationBridge } from './internalFederation'; import { AppListenerBridge } from './listeners'; +import { AppLivechatBridge } from './livechat'; import { AppMessageBridge } from './messages'; +import { AppModerationBridge } from './moderation'; +import { AppOAuthAppsBridge } from './oauthApps'; import { AppPersistenceBridge } from './persistence'; +import { AppRoleBridge } from './roles'; import { AppRoomBridge } from './rooms'; -import { AppInternalBridge } from './internal'; +import { AppSchedulerBridge } from './scheduler'; import { AppSettingBridge } from './settings'; -import { AppUserBridge } from './users'; -import { AppLivechatBridge } from './livechat'; -import { AppUploadBridge } from './uploads'; +import { AppThreadBridge } from './thread'; import { UiInteractionBridge } from './uiInteraction'; -import { AppSchedulerBridge } from './scheduler'; +import { AppUploadBridge } from './uploads'; +import { AppUserBridge } from './users'; import { AppVideoConferenceBridge } from './videoConferences'; -import { AppOAuthAppsBridge } from './oauthApps'; -import { AppInternalFederationBridge } from './internalFederation'; -import { AppModerationBridge } from './moderation'; -import { AppThreadBridge } from './thread'; export class RealAppBridges extends AppBridges { constructor(orch) { @@ -51,6 +52,7 @@ export class RealAppBridges extends AppBridges { this._internalFedBridge = new AppInternalFederationBridge(); this._moderationBridge = new AppModerationBridge(orch); this._threadBridge = new AppThreadBridge(orch); + this._roleBridge = new AppRoleBridge(orch); } getCommandBridge() { @@ -144,4 +146,8 @@ export class RealAppBridges extends AppBridges { getModerationBridge() { return this._moderationBridge; } + + getRoleBridge() { + return this._roleBridge; + } } diff --git a/apps/meteor/app/apps/server/bridges/cloud.ts b/apps/meteor/app/apps/server/bridges/cloud.ts index 9dcb9025044b..a0675c115f01 100644 --- a/apps/meteor/app/apps/server/bridges/cloud.ts +++ b/apps/meteor/app/apps/server/bridges/cloud.ts @@ -1,11 +1,10 @@ -import { CloudWorkspaceBridge } from '@rocket.chat/apps-engine/server/bridges/CloudWorkspaceBridge'; import type { IWorkspaceToken } from '@rocket.chat/apps-engine/definition/cloud/IWorkspaceToken'; +import { CloudWorkspaceBridge } from '@rocket.chat/apps-engine/server/bridges/CloudWorkspaceBridge'; -import { getWorkspaceAccessTokenWithScope } from '../../../cloud/server'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; +import { getWorkspaceAccessTokenWithScope } from '../../../cloud/server'; export class AppCloudBridge extends CloudWorkspaceBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/commands.ts b/apps/meteor/app/apps/server/bridges/commands.ts index 35c2bb253b32..ee7e73e16eb5 100644 --- a/apps/meteor/app/apps/server/bridges/commands.ts +++ b/apps/meteor/app/apps/server/bridges/commands.ts @@ -1,18 +1,17 @@ -import { Meteor } from 'meteor/meteor'; import type { ISlashCommand, ISlashCommandPreview, ISlashCommandPreviewItem } from '@rocket.chat/apps-engine/definition/slashcommands'; import { SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashcommands'; import { CommandBridge } from '@rocket.chat/apps-engine/server/bridges/CommandBridge'; import type { IMessage, RequiredField, SlashCommand, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../../utils/server'; import { Utilities } from '../../../../ee/lib/misc/Utilities'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { parseParameters } from '../../../../lib/utils/parseParameters'; +import { slashCommands } from '../../../utils/server/slashCommand'; export class AppCommandsBridge extends CommandBridge { disabledCommands: Map; - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); this.disabledCommands = new Map(); diff --git a/apps/meteor/app/apps/server/bridges/details.ts b/apps/meteor/app/apps/server/bridges/details.ts index cb60cc56f3aa..50709917183a 100644 --- a/apps/meteor/app/apps/server/bridges/details.ts +++ b/apps/meteor/app/apps/server/bridges/details.ts @@ -4,7 +4,6 @@ import { AppDetailChangesBridge as DetailChangesBridge } from '@rocket.chat/apps import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppDetailChangesBridge extends DetailChangesBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/environmental.ts b/apps/meteor/app/apps/server/bridges/environmental.ts index e159ad277998..43b34674e95f 100644 --- a/apps/meteor/app/apps/server/bridges/environmental.ts +++ b/apps/meteor/app/apps/server/bridges/environmental.ts @@ -5,7 +5,6 @@ import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestra export class AppEnvironmentalVariableBridge extends EnvironmentalVariableBridge { allowed: Array; - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); this.allowed = ['NODE_ENV', 'ROOT_URL', 'INSTANCE_IP']; diff --git a/apps/meteor/app/apps/server/bridges/http.ts b/apps/meteor/app/apps/server/bridges/http.ts index 6d6ee9ec358e..a4b09e848c90 100644 --- a/apps/meteor/app/apps/server/bridges/http.ts +++ b/apps/meteor/app/apps/server/bridges/http.ts @@ -1,6 +1,6 @@ -import { HttpBridge } from '@rocket.chat/apps-engine/server/bridges/HttpBridge'; import type { IHttpResponse } from '@rocket.chat/apps-engine/definition/accessors'; import type { IHttpBridgeRequestInfo } from '@rocket.chat/apps-engine/server/bridges'; +import { HttpBridge } from '@rocket.chat/apps-engine/server/bridges/HttpBridge'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; @@ -13,7 +13,6 @@ const isGetOrHead = (method: string): boolean => ['GET', 'HEAD'].includes(method const DEFAULT_TIMEOUT = 3 * 60 * 1000; export class AppHttpBridge extends HttpBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/internal.ts b/apps/meteor/app/apps/server/bridges/internal.ts index 3f906c0d8700..b41ffbb2889e 100644 --- a/apps/meteor/app/apps/server/bridges/internal.ts +++ b/apps/meteor/app/apps/server/bridges/internal.ts @@ -1,5 +1,5 @@ -import { InternalBridge } from '@rocket.chat/apps-engine/server/bridges/InternalBridge'; import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; +import { InternalBridge } from '@rocket.chat/apps-engine/server/bridges/InternalBridge'; import type { ISubscription } from '@rocket.chat/core-typings'; import { Settings, Subscriptions } from '@rocket.chat/models'; @@ -8,7 +8,6 @@ import { isTruthy } from '../../../../lib/isTruthy'; import { deasyncPromise } from '../../../../server/deasync/deasync'; export class AppInternalBridge extends InternalBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/listeners.js b/apps/meteor/app/apps/server/bridges/listeners.js index d81832c7fdf9..b1ee4fd14521 100644 --- a/apps/meteor/app/apps/server/bridges/listeners.js +++ b/apps/meteor/app/apps/server/bridges/listeners.js @@ -1,5 +1,5 @@ -import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata'; import { LivechatTransferEventType } from '@rocket.chat/apps-engine/definition/livechat'; +import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata'; export class AppListenerBridge { constructor(orch) { @@ -7,7 +7,6 @@ export class AppListenerBridge { } async handleEvent(event, ...payload) { - // eslint-disable-next-line complexity const method = (() => { switch (event) { case AppInterface.IPreMessageSentPrevent: diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index c4d6d9099643..71f7387e1aa5 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -1,5 +1,4 @@ -import { Random } from '@rocket.chat/random'; -import { LivechatBridge } from '@rocket.chat/apps-engine/server/bridges/LivechatBridge'; +import type { IExtraRoomParams } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator'; import type { ILivechatMessage, IVisitor, @@ -7,23 +6,23 @@ import type { ILivechatTransferData, IDepartment, } from '@rocket.chat/apps-engine/definition/livechat'; +import type { IMessage as IAppsEngineMesage } from '@rocket.chat/apps-engine/definition/messages'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; -import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; -import type { IExtraRoomParams } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator'; +import { LivechatBridge } from '@rocket.chat/apps-engine/server/bridges/LivechatBridge'; import type { SelectedAgent } from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, LivechatDepartment, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; -import { getRoom } from '../../../livechat/server/api/lib/livechat'; -import { Livechat } from '../../../livechat/server/lib/Livechat'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; -import { Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped'; import { callbacks } from '../../../../lib/callbacks'; import { deasyncPromise } from '../../../../server/deasync/deasync'; +import { getRoom } from '../../../livechat/server/api/lib/livechat'; +import { Livechat } from '../../../livechat/server/lib/Livechat'; +import { Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped'; import { settings } from '../../../settings/server'; export class AppLivechatBridge extends LivechatBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } @@ -281,7 +280,7 @@ export class AppLivechatBridge extends LivechatBridge { return Promise.all((await LivechatDepartment.findEnabledWithAgents().toArray()).map(boundConverter)); } - protected async _fetchLivechatRoomMessages(appId: string, roomId: string): Promise> { + protected async _fetchLivechatRoomMessages(appId: string, roomId: string): Promise> { this.orch.debugLog(`The App ${appId} is getting the transcript for livechat room ${roomId}.`); const messageConverter = this.orch.getConverters()?.get('messages'); @@ -289,9 +288,9 @@ export class AppLivechatBridge extends LivechatBridge { throw new Error('Could not get the message converter to process livechat room messages'); } - const boundMessageConverter = messageConverter.convertMessage.bind(messageConverter); + const livechatMessages = await Livechat.getRoomMessages({ rid: roomId }); - return (await Livechat.getRoomMessages({ rid: roomId })).map(boundMessageConverter); + return Promise.all(livechatMessages.map((message) => messageConverter.convertMessage(message) as Promise)); } protected async setCustomFields( diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index f6a50a9e6ab8..d75a0c244674 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -1,19 +1,18 @@ -import type { ITypingDescriptor } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; -import { MessageBridge } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; -import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; +import type { IUser } from '@rocket.chat/apps-engine/definition/users'; +import type { ITypingDescriptor } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; +import { MessageBridge } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; import { api } from '@rocket.chat/core-services'; import { Users, Subscriptions, Messages } from '@rocket.chat/models'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; +import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; import { updateMessage } from '../../../lib/server/functions/updateMessage'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; import notifications from '../../../notifications/server/lib/Notifications'; -import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; -import { deleteMessage } from '../../../lib/server'; export class AppMessageBridge extends MessageBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/moderation.ts b/apps/meteor/app/apps/server/bridges/moderation.ts index 68d2260c5714..4581f9a6ac6b 100644 --- a/apps/meteor/app/apps/server/bridges/moderation.ts +++ b/apps/meteor/app/apps/server/bridges/moderation.ts @@ -1,7 +1,7 @@ -import { ModerationBridge } from '@rocket.chat/apps-engine/server/bridges/ModerationBridge'; -import { ModerationReports } from '@rocket.chat/models'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; +import { ModerationBridge } from '@rocket.chat/apps-engine/server/bridges/ModerationBridge'; +import { ModerationReports } from '@rocket.chat/models'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { reportMessage } from '../../../../server/lib/moderation/reportMessage'; @@ -32,7 +32,7 @@ export class AppModerationBridge extends ModerationBridge { throw new Error('Invalid message id'); } - await ModerationReports.hideReportsByMessageId(messageId, appId, reason, action); + await ModerationReports.hideMessageReportsByMessageId(messageId, appId, reason, action); } protected async dismissReportsByUserId(userId: IUser['id'], reason: string, action: string, appId: string): Promise { @@ -41,6 +41,6 @@ export class AppModerationBridge extends ModerationBridge { if (!userId) { throw new Error('Invalid user id'); } - await ModerationReports.hideReportsByUserId(userId, appId, reason, action); + await ModerationReports.hideMessageReportsByUserId(userId, appId, reason, action); } } diff --git a/apps/meteor/app/apps/server/bridges/persistence.ts b/apps/meteor/app/apps/server/bridges/persistence.ts index 3206a04708b1..6876a9bcc79f 100644 --- a/apps/meteor/app/apps/server/bridges/persistence.ts +++ b/apps/meteor/app/apps/server/bridges/persistence.ts @@ -1,10 +1,9 @@ -import { PersistenceBridge } from '@rocket.chat/apps-engine/server/bridges/PersistenceBridge'; import type { RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; +import { PersistenceBridge } from '@rocket.chat/apps-engine/server/bridges/PersistenceBridge'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppPersistenceBridge extends PersistenceBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/roles.ts b/apps/meteor/app/apps/server/bridges/roles.ts new file mode 100644 index 000000000000..f973b7f49ed2 --- /dev/null +++ b/apps/meteor/app/apps/server/bridges/roles.ts @@ -0,0 +1,33 @@ +import type { IRole } from '@rocket.chat/apps-engine/definition/roles'; +import { RoleBridge } from '@rocket.chat/apps-engine/server/bridges'; +import { Roles } from '@rocket.chat/models'; + +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; + +export class AppRoleBridge extends RoleBridge { + constructor(private readonly orch: AppServerOrchestrator) { + super(); + } + + protected async getOneByIdOrName(idOrName: IRole['id'] | IRole['name'], appId: string): Promise { + this.orch.debugLog(`The App ${appId} is getting the roleByIdOrName: "${idOrName}"`); + + const role = await Roles.findOneByIdOrName(idOrName); + return this.orch.getConverters()?.get('roles').convertRole(role); + } + + protected async getCustomRoles(appId: string): Promise> { + this.orch.debugLog(`The App ${appId} is getting the custom roles`); + + const cursor = Roles.findCustomRoles(); + + const roles: IRole[] = []; + + for await (const role of cursor) { + const convRole = await this.orch.getConverters()?.get('roles').convertRole(role); + roles.push(convRole); + } + + return roles; + } +} diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index 0ca2d6d9e4b0..481292d61790 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -1,21 +1,20 @@ +import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; -import { RoomBridge } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; -import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; +import { RoomBridge } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; import type { ISubscription, IUser as ICoreUser, IRoom as ICoreRoom } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; +import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; +import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; import { createChannelMethod } from '../../../lib/server/methods/createChannel'; import { createPrivateGroupMethod } from '../../../lib/server/methods/createPrivateGroup'; -import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; -import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; export class AppRoomBridge extends RoomBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/scheduler.ts b/apps/meteor/app/apps/server/bridges/scheduler.ts index 8349f5764b41..0b2995166e63 100644 --- a/apps/meteor/app/apps/server/bridges/scheduler.ts +++ b/apps/meteor/app/apps/server/bridges/scheduler.ts @@ -1,10 +1,10 @@ import type { Job } from '@rocket.chat/agenda'; import { Agenda } from '@rocket.chat/agenda'; -import { ObjectID } from 'bson'; -import { MongoInternals } from 'meteor/mongo'; import type { IProcessor, IOnetimeSchedule, IRecurringSchedule, IJobContext } from '@rocket.chat/apps-engine/definition/scheduler'; import { StartupType } from '@rocket.chat/apps-engine/definition/scheduler'; import { SchedulerBridge } from '@rocket.chat/apps-engine/server/bridges/SchedulerBridge'; +import { ObjectID } from 'bson'; +import { MongoInternals } from 'meteor/mongo'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; @@ -36,7 +36,6 @@ export class AppSchedulerBridge extends SchedulerBridge { private scheduler: Agenda; - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); this.scheduler = new Agenda({ diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index 14e4834c3632..d61de9e8eca5 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -5,7 +5,6 @@ import { Settings } from '@rocket.chat/models'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppSettingBridge extends ServerSettingBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/thread.ts b/apps/meteor/app/apps/server/bridges/thread.ts index 88557fb60a42..313f60ecc4ac 100644 --- a/apps/meteor/app/apps/server/bridges/thread.ts +++ b/apps/meteor/app/apps/server/bridges/thread.ts @@ -1,10 +1,9 @@ -import { ThreadBridge } from '@rocket.chat/apps-engine/server/bridges/ThreadBridge'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; +import { ThreadBridge } from '@rocket.chat/apps-engine/server/bridges/ThreadBridge'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppThreadBridge extends ThreadBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/uiInteraction.ts b/apps/meteor/app/apps/server/bridges/uiInteraction.ts index 4d2c18412df9..b51c3be8ae3b 100644 --- a/apps/meteor/app/apps/server/bridges/uiInteraction.ts +++ b/apps/meteor/app/apps/server/bridges/uiInteraction.ts @@ -1,12 +1,11 @@ -import { UiInteractionBridge as UiIntBridge } from '@rocket.chat/apps-engine/server/bridges/UiInteractionBridge'; import type { IUIKitInteraction } from '@rocket.chat/apps-engine/definition/uikit'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; +import { UiInteractionBridge as UiIntBridge } from '@rocket.chat/apps-engine/server/bridges/UiInteractionBridge'; import { api } from '@rocket.chat/core-services'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class UiInteractionBridge extends UiIntBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/uploads.ts b/apps/meteor/app/apps/server/bridges/uploads.ts index 23148c7dab18..df16704a3196 100644 --- a/apps/meteor/app/apps/server/bridges/uploads.ts +++ b/apps/meteor/app/apps/server/bridges/uploads.ts @@ -1,12 +1,12 @@ -import { UploadBridge } from '@rocket.chat/apps-engine/server/bridges/UploadBridge'; import type { IUpload } from '@rocket.chat/apps-engine/definition/uploads'; import type { IUploadDetails } from '@rocket.chat/apps-engine/definition/uploads/IUploadDetails'; +import { UploadBridge } from '@rocket.chat/apps-engine/server/bridges/UploadBridge'; -import { FileUpload } from '../../../file-upload/server'; import { determineFileType } from '../../../../ee/lib/misc/determineFileType'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; -import { sendFileLivechatMessage } from '../../../livechat/server/methods/sendFileLivechatMessage'; +import { FileUpload } from '../../../file-upload/server'; import { sendFileMessage } from '../../../file-upload/server/methods/sendFileMessage'; +import { sendFileLivechatMessage } from '../../../livechat/server/methods/sendFileLivechatMessage'; const getUploadDetails = (details: IUploadDetails): Partial => { if (details.visitorToken) { @@ -16,7 +16,6 @@ const getUploadDetails = (details: IUploadDetails): Partial => { return details; }; export class AppUploadBridge extends UploadBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/users.ts b/apps/meteor/app/apps/server/bridges/users.ts index 27bac79b1030..d3c7dbc2a3d2 100644 --- a/apps/meteor/app/apps/server/bridges/users.ts +++ b/apps/meteor/app/apps/server/bridges/users.ts @@ -1,17 +1,18 @@ -import { Random } from '@rocket.chat/random'; -import { UserBridge } from '@rocket.chat/apps-engine/server/bridges/UserBridge'; import type { IUserCreationOptions, IUser, UserType } from '@rocket.chat/apps-engine/definition/users'; -import { Subscriptions, Users } from '@rocket.chat/models'; +import { UserBridge } from '@rocket.chat/apps-engine/server/bridges/UserBridge'; import { Presence } from '@rocket.chat/core-services'; import type { UserStatus } from '@rocket.chat/core-typings'; +import { Subscriptions, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; -import { setUserAvatar, deleteUser, getUserCreatedByApp } from '../../../lib/server/functions'; -import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; +import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; +import { deleteUser } from '../../../lib/server/functions/deleteUser'; +import { getUserCreatedByApp } from '../../../lib/server/functions/getUserCreatedByApp'; import { setUserActiveStatus } from '../../../lib/server/functions/setUserActiveStatus'; +import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar'; export class AppUserBridge extends UserBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/bridges/videoConferences.ts b/apps/meteor/app/apps/server/bridges/videoConferences.ts index c982d16087f9..c70f7f562fab 100644 --- a/apps/meteor/app/apps/server/bridges/videoConferences.ts +++ b/apps/meteor/app/apps/server/bridges/videoConferences.ts @@ -1,6 +1,6 @@ -import { VideoConferenceBridge } from '@rocket.chat/apps-engine/server/bridges/VideoConferenceBridge'; -import type { AppVideoConference, VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; import type { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/videoConfProviders'; +import type { AppVideoConference, VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; +import { VideoConferenceBridge } from '@rocket.chat/apps-engine/server/bridges/VideoConferenceBridge'; import { VideoConf } from '@rocket.chat/core-services'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; @@ -8,7 +8,6 @@ import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; import type { AppVideoConferencesConverter } from '../converters/videoConferences'; export class AppVideoConferenceBridge extends VideoConferenceBridge { - // eslint-disable-next-line no-empty-function constructor(private readonly orch: AppServerOrchestrator) { super(); } diff --git a/apps/meteor/app/apps/server/converters/index.ts b/apps/meteor/app/apps/server/converters/index.ts index c1f60ff18162..96716af03ca7 100644 --- a/apps/meteor/app/apps/server/converters/index.ts +++ b/apps/meteor/app/apps/server/converters/index.ts @@ -1,10 +1,11 @@ +import { AppDepartmentsConverter } from './departments'; import { AppMessagesConverter } from './messages'; +import { AppRolesConverter } from './roles'; import { AppRoomsConverter } from './rooms'; import { AppSettingsConverter } from './settings'; +import { AppUploadsConverter } from './uploads'; import { AppUsersConverter } from './users'; import { AppVideoConferencesConverter } from './videoConferences'; -import { AppDepartmentsConverter } from './departments'; -import { AppUploadsConverter } from './uploads'; import { AppVisitorsConverter } from './visitors'; export { @@ -16,4 +17,5 @@ export { AppDepartmentsConverter, AppUploadsConverter, AppVisitorsConverter, + AppRolesConverter, }; diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 8eae8eb5954d..6243bd2c603e 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -1,5 +1,5 @@ -import { Random } from '@rocket.chat/random'; import { Messages, Rooms, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; diff --git a/apps/meteor/app/apps/server/converters/roles.ts b/apps/meteor/app/apps/server/converters/roles.ts new file mode 100644 index 000000000000..4ac1f3956420 --- /dev/null +++ b/apps/meteor/app/apps/server/converters/roles.ts @@ -0,0 +1,29 @@ +import type { IRole as AppsEngineRole } from '@rocket.chat/apps-engine/definition/roles'; +import type { IRole } from '@rocket.chat/core-typings'; +import { Roles } from '@rocket.chat/models'; + +import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; + +export class AppRolesConverter { + async convertById(roleId: string): Promise { + const role = await Roles.findOneById(roleId); + + if (!role) { + return; + } + return this.convertRole(role); + } + + async convertRole(role: IRole): Promise { + const map = { + id: '_id', + name: 'name', + description: 'description', + mandatory2fa: 'mandatory2fa', + protected: 'protected', + scope: 'scope', + }; + + return (await transformMappedData(role, map)) as unknown as AppsEngineRole; + } +} diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 44d513bd591d..ae38feff5eff 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -39,8 +39,8 @@ export class AppRoomsConverter { if (room.visitor) { const visitor = await LivechatVisitors.findOneById(room.visitor.id); - const lastMessageTs = room?.visitor?.lastMessageTs; - const phone = room?.visitor?.channelPhone; + const { lastMessageTs, phone } = room.visitorChannelInfo; + v = { _id: visitor._id, username: visitor.username, @@ -53,7 +53,7 @@ export class AppRoomsConverter { let departmentId; if (room.department) { - const department = await LivechatDepartment.findOneById(room.department.id); + const department = await LivechatDepartment.findOneById(room.department.id, { projection: { _id: 1 } }); departmentId = department._id; } @@ -170,26 +170,32 @@ export class AppRoomsConverter { return this.orch.getConverters().get('users').convertById(u._id); }, - visitor: async (room) => { + visitor: (room) => { const { v } = room; if (!v) { return undefined; } - const { lastMessageTs, phone } = v; + return this.orch.getConverters().get('visitors').convertById(v._id); + }, + // Note: room.v is not just visitor, it also contains channel related visitor data + // so we need to pass this data to the converter + // So suppose you have a contact whom we're contacting using SMS via 2 phone no's, + // let's call X and Y. Then if the contact sends a message using X phone number, + // then room.v.phoneNo would be X and correspondingly we'll store the timestamp of + // the last message from this visitor from X phone no on room.v.lastMessageTs + visitorChannelInfo: (room) => { + const { v } = room; - delete room.v; + if (!v) { + return undefined; + } + + const { lastMessageTs, phone } = v; return { - ...(await this.orch.getConverters().get('visitors').convertById(v._id)), - // Note: room.v is not just visitor, it also contains channel related visitor data - // so we need to pass this data to the converter - // So suppose you have a contact whom we're contacting using SMS via 2 phone no's, - // let's call X and Y. Then if the contact sends a message using X phone number, - // then room.v.phoneNo would be X and correspondingly we'll store the timestamp of - // the last message from this visitor from X phone no on room.v.lastMessageTs - ...(phone && { channelPhone: phone }), + ...(phone && { phone }), ...(lastMessageTs && { lastMessageTs }), }; }, diff --git a/apps/meteor/app/apps/server/converters/threads.ts b/apps/meteor/app/apps/server/converters/threads.ts index bfeb8d42865b..1fa52d29cb16 100644 --- a/apps/meteor/app/apps/server/converters/threads.ts +++ b/apps/meteor/app/apps/server/converters/threads.ts @@ -1,7 +1,7 @@ -import { isEditedMessage, type IMessage } from '@rocket.chat/core-typings'; -import { Messages } from '@rocket.chat/models'; import type { IMessage as AppsEngineMessage } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; +import { isEditedMessage, type IMessage } from '@rocket.chat/core-typings'; +import { Messages } from '@rocket.chat/models'; import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; diff --git a/apps/meteor/app/apps/server/converters/videoConferences.ts b/apps/meteor/app/apps/server/converters/videoConferences.ts index 0b0c0a573b76..00eb4e915137 100644 --- a/apps/meteor/app/apps/server/converters/videoConferences.ts +++ b/apps/meteor/app/apps/server/converters/videoConferences.ts @@ -1,6 +1,6 @@ import type { VideoConference } from '@rocket.chat/apps-engine/definition/videoConferences'; -import type { IVideoConference } from '@rocket.chat/core-typings'; import { VideoConf } from '@rocket.chat/core-services'; +import type { IVideoConference } from '@rocket.chat/core-typings'; export class AppVideoConferencesConverter { async convertById(callId: string): Promise { diff --git a/apps/meteor/app/assets/server/assets.ts b/apps/meteor/app/assets/server/assets.ts index ef008761dfbf..726d16ac34bf 100644 --- a/apps/meteor/app/assets/server/assets.ts +++ b/apps/meteor/app/assets/server/assets.ts @@ -1,23 +1,23 @@ import crypto from 'crypto'; import type { ServerResponse, IncomingMessage } from 'http'; +import type { IRocketChatAssets, IRocketChatAsset, IRocketChatAssetCache } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { NextHandleFunction } from 'connect'; +import sizeOf from 'image-size'; import { Meteor } from 'meteor/meteor'; import { WebApp, WebAppInternals } from 'meteor/webapp'; import { WebAppHashing } from 'meteor/webapp-hashing'; -import _ from 'underscore'; -import sizeOf from 'image-size'; import sharp from 'sharp'; -import type { NextHandleFunction } from 'connect'; -import type { IRocketChatAssets, IRocketChatAsset, IRocketChatAssetCache } from '@rocket.chat/core-typings'; -import { Settings } from '@rocket.chat/models'; +import _ from 'underscore'; -import { settings, settingsRegistry } from '../../settings/server'; -import { getURL } from '../../utils/server/getURL'; -import { getExtension } from '../../utils/lib/mimeTypes'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { RocketChatFile } from '../../file/server'; import { methodDeprecationLogger } from '../../lib/server/lib/deprecationWarningLogger'; +import { settings, settingsRegistry } from '../../settings/server'; +import { getExtension } from '../../utils/lib/mimeTypes'; +import { getURL } from '../../utils/server/getURL'; const RocketChatAssetsInstance = new RocketChatFile.GridFS({ name: 'assets', @@ -234,8 +234,8 @@ class RocketChatAssetsClass { await RocketChatAssetsInstance.deleteFile(asset); const ws = RocketChatAssetsInstance.createWriteStream(asset, contentType); - ws.on('end', function () { - return setTimeout(async function () { + ws.on('end', () => { + return setTimeout(async () => { const key = `Assets_${asset}`; const value = { url: `assets/${asset}.${extension}`, @@ -243,7 +243,6 @@ class RocketChatAssetsClass { }; void Settings.updateValueById(key, value); - // eslint-disable-next-line @typescript-eslint/no-use-before-define return RocketChatAssets.processAsset(key, value); }, 200); }); @@ -265,7 +264,6 @@ class RocketChatAssetsClass { }; void Settings.updateValueById(key, value); - // eslint-disable-next-line @typescript-eslint/no-use-before-define await RocketChatAssets.processAsset(key, value); } diff --git a/apps/meteor/app/authentication/server/ILoginAttempt.ts b/apps/meteor/app/authentication/server/ILoginAttempt.ts index f48aeba7d073..c4c89f71f27c 100644 --- a/apps/meteor/app/authentication/server/ILoginAttempt.ts +++ b/apps/meteor/app/authentication/server/ILoginAttempt.ts @@ -1,3 +1,4 @@ +import type { MeteorError } from '@rocket.chat/core-services'; import type { IUser, IMethodConnection } from '@rocket.chat/core-typings'; interface IMethodArgument { @@ -22,4 +23,5 @@ export interface ILoginAttempt { methodArguments: IMethodArgument[]; connection: IMethodConnection; user?: IUser; + error?: MeteorError; } diff --git a/apps/meteor/app/authentication/server/hooks/login.ts b/apps/meteor/app/authentication/server/hooks/login.ts index 6770568ea918..10b91271c88f 100644 --- a/apps/meteor/app/authentication/server/hooks/login.ts +++ b/apps/meteor/app/authentication/server/hooks/login.ts @@ -1,13 +1,20 @@ import { Accounts } from 'meteor/accounts-base'; -import type { ILoginAttempt } from '../ILoginAttempt'; -import { saveFailedLoginAttempts, saveSuccessfulLogin } from '../lib/restrictLoginAttempts'; -import { logFailedLoginAttempts } from '../lib/logLoginAttempts'; import { callbacks } from '../../../../lib/callbacks'; import { settings } from '../../../settings/server'; +import type { ILoginAttempt } from '../ILoginAttempt'; +import { logFailedLoginAttempts } from '../lib/logLoginAttempts'; +import { saveFailedLoginAttempts, saveSuccessfulLogin } from '../lib/restrictLoginAttempts'; + +const ignoredErrorTypes = ['totp-required', 'error-login-blocked-for-user']; Accounts.onLoginFailure(async (login: ILoginAttempt) => { - if (settings.get('Block_Multiple_Failed_Logins_Enabled')) { + // do not save specific failed login attempts + if ( + settings.get('Block_Multiple_Failed_Logins_Enabled') && + login.error?.error && + !ignoredErrorTypes.includes(String(login.error.error)) + ) { await saveFailedLoginAttempts(login); } diff --git a/apps/meteor/app/authentication/server/lib/logLoginAttempts.ts b/apps/meteor/app/authentication/server/lib/logLoginAttempts.ts index 806b4f93b80c..0f97796aa4a7 100644 --- a/apps/meteor/app/authentication/server/lib/logLoginAttempts.ts +++ b/apps/meteor/app/authentication/server/lib/logLoginAttempts.ts @@ -1,6 +1,6 @@ -import type { ILoginAttempt } from '../ILoginAttempt'; -import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; +import type { ILoginAttempt } from '../ILoginAttempt'; export const logFailedLoginAttempts = (login: ILoginAttempt): void => { if (!settings.get('Login_Logs_Enabled')) { diff --git a/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts b/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts index 743521df6721..3b6cd6c89409 100644 --- a/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts +++ b/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts @@ -1,12 +1,11 @@ import type { IServerEvent } from '@rocket.chat/core-typings'; import { ServerEventType } from '@rocket.chat/core-typings'; -import { Rooms, ServerEvents, Sessions, Users } from '@rocket.chat/models'; -import moment from 'moment'; +import { Logger } from '@rocket.chat/logger'; +import { Rooms, ServerEvents, Users } from '@rocket.chat/models'; import { addMinutesToADate } from '../../../../lib/utils/addMinutesToADate'; import { getClientAddress } from '../../../../server/lib/getClientAddress'; -import { sendMessage } from '../../../lib/server/functions'; -import { Logger } from '../../../logger/server'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { settings } from '../../../settings/server'; import type { ILoginAttempt } from '../ILoginAttempt'; @@ -58,36 +57,42 @@ export const isValidLoginAttemptByIp = async (ip: string): Promise => { return true; } - const lastLogin = await Sessions.findLastLoginByIp(ip); - let failedAttemptsSinceLastLogin; - - if (!lastLogin?.loginAt) { - failedAttemptsSinceLastLogin = await ServerEvents.countFailedAttemptsByIp(ip); - } else { - failedAttemptsSinceLastLogin = await ServerEvents.countFailedAttemptsByIpSince(ip, new Date(lastLogin.loginAt)); - } - + // misconfigured const attemptsUntilBlock = settings.get('Block_Multiple_Failed_Logins_Attempts_Until_Block_By_Ip'); + if (!attemptsUntilBlock) { + return true; + } - if (attemptsUntilBlock && failedAttemptsSinceLastLogin < attemptsUntilBlock) { + // if user never failed to login, then it's valid + const lastFailedAttemptAt = (await ServerEvents.findLastFailedAttemptByIp(ip))?.ts; + if (!lastFailedAttemptAt) { return true; } - const lastAttemptAt = (await ServerEvents.findLastFailedAttemptByIp(ip))?.ts; + const minutesUntilUnblock = settings.get('Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes'); - if (!lastAttemptAt) { + const lockoutTimeStart = addMinutesToADate(new Date(), minutesUntilUnblock * -1); + const lastSuccessfulAttemptAt = (await ServerEvents.findLastSuccessfulAttemptByIp(ip))?.ts; + + // successful logins should reset the counter + const startTime = lastSuccessfulAttemptAt + ? new Date(Math.max(lockoutTimeStart.getTime(), lastSuccessfulAttemptAt.getTime())) + : lockoutTimeStart; + + const failedAttemptsSinceLastLogin = await ServerEvents.countFailedAttemptsByIpSince(ip, startTime); + + // if user didn't reach the threshold, then it's valid + if (failedAttemptsSinceLastLogin < attemptsUntilBlock) { return true; } - const minutesUntilUnblock = settings.get('Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes') as number; - const willBeBlockedUntil = addMinutesToADate(new Date(lastAttemptAt), minutesUntilUnblock); - const isValid = moment(new Date()).isSameOrAfter(willBeBlockedUntil); + if (settings.get('Block_Multiple_Failed_Logins_Notify_Failed')) { + const willBeBlockedUntil = addMinutesToADate(new Date(lastFailedAttemptAt), minutesUntilUnblock); - if (settings.get('Block_Multiple_Failed_Logins_Notify_Failed') && !isValid) { await notifyFailedLogin(ip, willBeBlockedUntil, failedAttemptsSinceLastLogin); } - return isValid; + return false; }; export const isValidAttemptByUser = async (login: ILoginAttempt): Promise => { @@ -96,41 +101,47 @@ export const isValidAttemptByUser = async (login: ILoginAttempt): Promise('Block_Multiple_Failed_Logins_Attempts_Until_Block_by_User'); + if (!attemptsUntilBlock) { + return true; + } - if (attemptsUntilBlock && failedAttemptsSinceLastLogin < attemptsUntilBlock) { + // if user never failed to login, then it's valid + const lastFailedAttemptAt = (await ServerEvents.findLastFailedAttemptByUsername(loginUsername))?.ts; + if (!lastFailedAttemptAt) { return true; } - const lastAttemptAt = (await ServerEvents.findLastFailedAttemptByUsername(user.username as string))?.ts; + const minutesUntilUnblock = settings.get('Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes'); + + const lockoutTimeStart = addMinutesToADate(new Date(), minutesUntilUnblock * -1); + const lastSuccessfulAttemptAt = (await ServerEvents.findLastSuccessfulAttemptByUsername(loginUsername))?.ts; + + // succesful logins should reset the counter + const startTime = lastSuccessfulAttemptAt + ? new Date(Math.max(lockoutTimeStart.getTime(), lastSuccessfulAttemptAt.getTime())) + : lockoutTimeStart; + + // get total failed attempts during the lockout time + const failedAttemptsSinceLastLogin = await ServerEvents.countFailedAttemptsByUsernameSince(loginUsername, startTime); - if (!lastAttemptAt) { + // if user didn't reach the threshold, then it's valid + if (failedAttemptsSinceLastLogin < attemptsUntilBlock) { return true; } - const minutesUntilUnblock = settings.get('Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes') as number; - const willBeBlockedUntil = addMinutesToADate(new Date(lastAttemptAt), minutesUntilUnblock); - const isValid = moment(new Date()).isSameOrAfter(willBeBlockedUntil); + if (settings.get('Block_Multiple_Failed_Logins_Notify_Failed')) { + const willBeBlockedUntil = addMinutesToADate(new Date(lastFailedAttemptAt), minutesUntilUnblock); - if (settings.get('Block_Multiple_Failed_Logins_Notify_Failed') && !isValid) { - await notifyFailedLogin(user.username, willBeBlockedUntil, failedAttemptsSinceLastLogin); + await notifyFailedLogin(loginUsername, willBeBlockedUntil, failedAttemptsSinceLastLogin); } - return isValid; + return false; }; export const saveFailedLoginAttempts = async (login: ILoginAttempt): Promise => { diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index 3466c5741f1d..e3b97c1aae88 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -1,25 +1,26 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; +import { Roles, Settings, Users } from '@rocket.chat/models'; +import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; import { Accounts } from 'meteor/accounts-base'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; -import { Roles, Settings, Users } from '@rocket.chat/models'; -import * as Mailer from '../../../mailer/server/api'; -import { settings } from '../../../settings/server'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; -import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; -import { getAvatarSuggestionForUser } from '../../../lib/server/functions/getAvatarSuggestionForUser'; +import { beforeCreateUserCallback } from '../../../../lib/callbacks/beforeCreateUserCallback'; import { parseCSV } from '../../../../lib/utils/parseCSV'; -import { isValidAttemptByUser, isValidLoginAttemptByIp } from '../lib/restrictLoginAttempts'; +import { safeHtmlDots } from '../../../../lib/utils/safeHtmlDots'; import { getClientAddress } from '../../../../server/lib/getClientAddress'; +import { i18n } from '../../../../server/lib/i18n'; +import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; import { getNewUserRoles } from '../../../../server/services/user/lib/getNewUserRoles'; -import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; -import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser'; -import { safeHtmlDots } from '../../../../lib/utils/safeHtmlDots'; +import { getAvatarSuggestionForUser } from '../../../lib/server/functions/getAvatarSuggestionForUser'; import { joinDefaultChannels } from '../../../lib/server/functions/joinDefaultChannels'; import { setAvatarFromServiceWithValidation } from '../../../lib/server/functions/setUserAvatar'; -import { i18n } from '../../../../server/lib/i18n'; +import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; +import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser'; +import { isValidAttemptByUser, isValidLoginAttemptByIp } from '../lib/restrictLoginAttempts'; Accounts.config({ forbidClientAccountCreation: true, @@ -183,7 +184,7 @@ const validateEmailDomain = (user) => { const onCreateUserAsync = async function (options, user = {}) { if (!options.skipBeforeCreateUserCallback) { - await callbacks.run('beforeCreateUser', options, user); + await beforeCreateUserCallback.run(options, user); } user.status = 'offline'; @@ -248,11 +249,6 @@ const onCreateUserAsync = async function (options, user = {}) { await callbacks.run('onCreateUser', options, user); } - if (!options.skipAppsEngineEvent) { - // App IPostUserCreated event hook - await Apps.triggerEvent(AppEvents.IPostUserCreated, { user, performedBy: await safeGetMeteorUser() }); - } - if (!options.skipEmailValidation && !validateEmailDomain(user)) { throw new Meteor.Error(403, 'User validation failed'); } @@ -297,6 +293,11 @@ const insertUserDocAsync = async function (options, user) { }; } + // Make sure that the user has the field 'roles' + if (!user.roles) { + user.roles = []; + } + const _id = insertUserDoc.call(Accounts, options, user); user = await Users.findOne({ @@ -331,7 +332,7 @@ const insertUserDocAsync = async function (options, user) { } if (!options.skipAfterCreateUserCallback && user.type !== 'visitor') { - setImmediate(function () { + setImmediate(() => { return callbacks.run('afterCreateUser', user); }); } @@ -347,6 +348,13 @@ const insertUserDocAsync = async function (options, user) { } } + if (!options.skipAppsEngineEvent) { + // `post` triggered events don't need to wait for the promise to resolve + Apps.triggerEvent(AppEvents.IPostUserCreated, { user, performedBy: await safeGetMeteorUser() }).catch((e) => { + Apps.getRocketChatLogger().error('Error while executing post user created event:', e); + }); + } + return _id; }; @@ -406,7 +414,7 @@ const validateLoginAttemptAsync = async function (login) { login = await callbacks.run('onValidateLogin', login); await Users.updateLastLoginById(login.user._id); - setImmediate(function () { + setImmediate(() => { return callbacks.run('afterValidateLogin', login); }); @@ -427,7 +435,7 @@ Accounts.validateLoginAttempt(function (...args) { return Promise.await(validateLoginAttemptAsync.call(this, ...args)); }); -Accounts.validateNewUser(function (user) { +Accounts.validateNewUser((user) => { if (user.type === 'visitor') { return true; } @@ -443,6 +451,30 @@ Accounts.validateNewUser(function (user) { return true; }); +Accounts.validateNewUser((user) => { + if (user.type === 'visitor') { + return true; + } + + let domainWhiteList = settings.get('Accounts_AllowedDomainsList'); + if (_.isEmpty(domainWhiteList?.trim())) { + return true; + } + + domainWhiteList = domainWhiteList.split(',').map((domain) => domain.trim()); + + if (user.emails && user.emails.length > 0) { + const email = user.emails[0].address; + const inWhiteList = domainWhiteList.some((domain) => email.match(`@${escapeRegExp(domain)}$`)); + + if (inWhiteList === false) { + throw new Meteor.Error('error-invalid-domain'); + } + } + + return true; +}); + export const MAX_RESUME_LOGIN_TOKENS = parseInt(process.env.MAX_RESUME_LOGIN_TOKENS) || 50; Accounts.onLogin(async ({ user }) => { diff --git a/apps/meteor/app/authorization/client/hasPermission.ts b/apps/meteor/app/authorization/client/hasPermission.ts index 671fa44cb341..66171aeadd28 100644 --- a/apps/meteor/app/authorization/client/hasPermission.ts +++ b/apps/meteor/app/authorization/client/hasPermission.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import type { IUser, IRole, IPermission } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import * as Models from '../../models/client'; import { AuthorizationUtils } from '../lib/AuthorizationUtils'; diff --git a/apps/meteor/app/authorization/server/constant/permissions.ts b/apps/meteor/app/authorization/server/constant/permissions.ts index 0090d10fd880..fc917028c33f 100644 --- a/apps/meteor/app/authorization/server/constant/permissions.ts +++ b/apps/meteor/app/authorization/server/constant/permissions.ts @@ -69,6 +69,8 @@ export const permissions = [ { _id: 'view-c-room', roles: ['admin', 'user', 'bot', 'app', 'anonymous'] }, { _id: 'user-generate-access-token', roles: ['admin'] }, { _id: 'view-d-room', roles: ['admin', 'user', 'bot', 'app', 'guest'] }, + { _id: 'view-device-management', roles: ['admin'] }, + { _id: 'view-engagement-dashboard', roles: ['admin'] }, { _id: 'view-full-other-user-info', roles: ['admin'] }, { _id: 'view-history', roles: ['admin', 'user', 'anonymous'] }, { _id: 'view-joined-room', roles: ['guest', 'bot', 'app', 'anonymous'] }, diff --git a/apps/meteor/app/authorization/server/functions/canDeleteMessage.ts b/apps/meteor/app/authorization/server/functions/canDeleteMessage.ts index 369f26bbed1a..91769e71270a 100644 --- a/apps/meteor/app/authorization/server/functions/canDeleteMessage.ts +++ b/apps/meteor/app/authorization/server/functions/canDeleteMessage.ts @@ -1,8 +1,8 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Rooms } from '@rocket.chat/models'; -import { hasPermissionAsync } from './hasPermission'; import { getValue } from '../../../settings/server/raw'; +import { hasPermissionAsync } from './hasPermission'; const elapsedTime = (ts: Date): number => { const dif = Date.now() - ts.getTime(); diff --git a/apps/meteor/app/authorization/server/functions/canSendMessage.ts b/apps/meteor/app/authorization/server/functions/canSendMessage.ts index e2c3e27a9029..9cf5c5ec78d2 100644 --- a/apps/meteor/app/authorization/server/functions/canSendMessage.ts +++ b/apps/meteor/app/authorization/server/functions/canSendMessage.ts @@ -1,10 +1,10 @@ import type { IRoom, IUser } from '@rocket.chat/core-typings'; import { Subscriptions, Rooms } from '@rocket.chat/models'; +import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { canAccessRoomAsync } from './canAccessRoom'; import { hasPermissionAsync } from './hasPermission'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; const subscriptionOptions = { projection: { diff --git a/apps/meteor/app/authorization/server/functions/getUsersInRole.ts b/apps/meteor/app/authorization/server/functions/getUsersInRole.ts index 0e7da6ff3600..3b3af203221a 100644 --- a/apps/meteor/app/authorization/server/functions/getUsersInRole.ts +++ b/apps/meteor/app/authorization/server/functions/getUsersInRole.ts @@ -1,8 +1,8 @@ -import type { Document, FindCursor, FindOptions } from 'mongodb'; import type { IRole, IUser } from '@rocket.chat/core-typings'; -import { Roles, Subscriptions, Users } from '@rocket.chat/models'; import type { FindPaginated } from '@rocket.chat/model-typings'; +import { Roles, Subscriptions, Users } from '@rocket.chat/models'; import { compact } from 'lodash'; +import type { Document, FindCursor, FindOptions } from 'mongodb'; export function getUsersInRole(roleId: IRole['_id'], scope?: string): Promise>; diff --git a/apps/meteor/app/authorization/server/functions/hasPermission.ts b/apps/meteor/app/authorization/server/functions/hasPermission.ts index 8828764bec94..12cdfcd35bd7 100644 --- a/apps/meteor/app/authorization/server/functions/hasPermission.ts +++ b/apps/meteor/app/authorization/server/functions/hasPermission.ts @@ -1,5 +1,5 @@ -import type { IUser, IPermission, IRoom } from '@rocket.chat/core-typings'; import { Authorization } from '@rocket.chat/core-services'; +import type { IUser, IPermission, IRoom } from '@rocket.chat/core-typings'; export const hasAllPermissionAsync = async ( userId: IUser['_id'], diff --git a/apps/meteor/app/authorization/server/functions/upsertPermissions.ts b/apps/meteor/app/authorization/server/functions/upsertPermissions.ts index d48de450ac4b..bb908916c885 100644 --- a/apps/meteor/app/authorization/server/functions/upsertPermissions.ts +++ b/apps/meteor/app/authorization/server/functions/upsertPermissions.ts @@ -2,9 +2,9 @@ import type { IPermission, ISetting } from '@rocket.chat/core-typings'; import { Permissions, Settings } from '@rocket.chat/models'; +import { createOrUpdateProtectedRoleAsync } from '../../../../server/lib/roles/createOrUpdateProtectedRole'; import { settings } from '../../../settings/server'; import { getSettingPermissionId, CONSTANTS } from '../../lib'; -import { createOrUpdateProtectedRoleAsync } from '../../../../server/lib/roles/createOrUpdateProtectedRole'; import { permissions } from '../constant/permissions'; export const upsertPermissions = async (): Promise => { @@ -35,9 +35,7 @@ export const upsertPermissions = async (): Promise => { [key: string]: IPermission; } = {}; - const selector = { level: 'settings' as const, ...(settingId && { settingId }) }; - - await Permissions.find(selector).forEach(function (permission: IPermission) { + await Permissions.findByLevel('settings', settingId).forEach((permission: IPermission) => { previousSettingPermissions[permission._id] = permission; }); return previousSettingPermissions; @@ -113,7 +111,7 @@ export const upsertPermissions = async (): Promise => { await createPermissionsForExistingSettings(); // register a callback for settings for be create in higher-level-packages - settings.on('*', async function ([settingId]) { + settings.on('*', async ([settingId]) => { const previousSettingPermissions = await getPreviousPermissions(settingId); const setting = await Settings.findOneById(settingId); if (setting && !setting.hidden) { diff --git a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts index 33bf7eb196f6..cb6422a03142 100644 --- a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Permissions } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../functions/hasPermission'; import { CONSTANTS, AuthorizationUtils } from '../../lib'; +import { hasPermissionAsync } from '../functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/authorization/server/methods/addUserToRole.ts b/apps/meteor/app/authorization/server/methods/addUserToRole.ts index 2d9f526318a9..02ccc76373f1 100644 --- a/apps/meteor/app/authorization/server/methods/addUserToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addUserToRole.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; +import { api } from '@rocket.chat/core-services'; import type { IRole, IUser } from '@rocket.chat/core-typings'; import { Roles, Users } from '@rocket.chat/models'; -import { api } from '@rocket.chat/core-services'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../functions/hasPermission'; -import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/authorization/server/methods/deleteRole.ts b/apps/meteor/app/authorization/server/methods/deleteRole.ts index 98ffd7fae5b4..2ff09deadf68 100644 --- a/apps/meteor/app/authorization/server/methods/deleteRole.ts +++ b/apps/meteor/app/authorization/server/methods/deleteRole.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; import type { IRole } from '@rocket.chat/core-typings'; import { Roles } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; -import { hasPermissionAsync } from '../functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { hasPermissionAsync } from '../functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts index 123c6bfb1fdf..30a1b2a759b6 100644 --- a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts +++ b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Permissions } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../functions/hasPermission'; import { CONSTANTS } from '../../lib'; +import { hasPermissionAsync } from '../functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts index 1a739938da0c..d5dba40a1e53 100644 --- a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts +++ b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; +import { api } from '@rocket.chat/core-services'; import type { IRole, IUser } from '@rocket.chat/core-typings'; import { Roles, Users } from '@rocket.chat/models'; -import { api } from '@rocket.chat/core-services'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../functions/hasPermission'; -import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/authorization/server/streamer/permissions/index.ts b/apps/meteor/app/authorization/server/streamer/permissions/index.ts index a9e2073ff541..ff8fd3c93262 100644 --- a/apps/meteor/app/authorization/server/streamer/permissions/index.ts +++ b/apps/meteor/app/authorization/server/streamer/permissions/index.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check, Match } from 'meteor/check'; +import type { IPermission, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; import { Permissions } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IPermission, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; +import { check, Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { WithId } from 'mongodb'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/autotranslate/client/index.ts b/apps/meteor/app/autotranslate/client/index.ts index 4dd8f5ccdd9b..9cd1038ce86a 100644 --- a/apps/meteor/app/autotranslate/client/index.ts +++ b/apps/meteor/app/autotranslate/client/index.ts @@ -1,4 +1,3 @@ import './lib/actionButton'; -import './lib/tabBar'; export { AutoTranslate, createAutoTranslateMessageStreamHandler } from './lib/autotranslate'; diff --git a/apps/meteor/app/autotranslate/client/lib/actionButton.ts b/apps/meteor/app/autotranslate/client/lib/actionButton.ts index 14a81dbf9f97..24cea2d8d28a 100644 --- a/apps/meteor/app/autotranslate/client/lib/actionButton.ts +++ b/apps/meteor/app/autotranslate/client/lib/actionButton.ts @@ -1,18 +1,18 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { AutoTranslate } from './autotranslate'; -import { settings } from '../../../settings/client'; -import { hasAtLeastOnePermission } from '../../../authorization/client'; -import { MessageAction } from '../../../ui-utils/client/lib/MessageAction'; +import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; import { messageArgs } from '../../../../client/lib/utils/messageArgs'; -import { Messages } from '../../../models/client'; import { hasTranslationLanguageInAttachments, hasTranslationLanguageInMessage, } from '../../../../client/views/room/MessageList/lib/autoTranslate'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; +import { hasAtLeastOnePermission } from '../../../authorization/client'; +import { Messages } from '../../../models/client'; +import { settings } from '../../../settings/client'; +import { MessageAction } from '../../../ui-utils/client/lib/MessageAction'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { AutoTranslate } from './autotranslate'; Meteor.startup(() => { AutoTranslate.init(); @@ -24,6 +24,7 @@ Meteor.startup(() => { icon: 'language', label: 'Translate', context: ['message', 'message-mobile', 'threads'], + type: 'interaction', action(_, props) { const { message = messageArgs(this).msg } = props; const language = AutoTranslate.getLanguage(message.rid); @@ -58,6 +59,7 @@ Meteor.startup(() => { icon: 'language', label: 'View_original', context: ['message', 'message-mobile', 'threads'], + type: 'interaction', action(_, props) { const { message = messageArgs(this).msg } = props; const language = AutoTranslate.getLanguage(message.rid); diff --git a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts index edd3e78766bc..46b858c4d3ea 100644 --- a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts +++ b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts @@ -1,6 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; -import mem from 'mem'; import type { IRoom, ISubscription, @@ -10,13 +7,16 @@ import type { MessageAttachmentDefault, } from '@rocket.chat/core-typings'; import { isTranslatedMessageAttachment } from '@rocket.chat/core-typings'; +import mem from 'mem'; +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; -import { Subscriptions, Messages } from '../../../models/client'; -import { hasPermission } from '../../../authorization/client'; import { hasTranslationLanguageInAttachments, hasTranslationLanguageInMessage, } from '../../../../client/views/room/MessageList/lib/autoTranslate'; +import { hasPermission } from '../../../authorization/client'; +import { Subscriptions, Messages } from '../../../models/client'; import { sdk } from '../../../utils/client/lib/SDKClient'; let userLanguage = 'en'; diff --git a/apps/meteor/app/autotranslate/client/lib/tabBar.ts b/apps/meteor/app/autotranslate/client/lib/tabBar.ts deleted file mode 100644 index fe564c7bd6b0..000000000000 --- a/apps/meteor/app/autotranslate/client/lib/tabBar.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { lazy, useMemo } from 'react'; -import { useSetting, usePermission } from '@rocket.chat/ui-contexts'; - -import { addAction } from '../../../../client/views/room/lib/Toolbox'; - -addAction('autotranslate', () => { - const hasPermission = usePermission('auto-translate'); - const autoTranslateEnabled = useSetting('AutoTranslate_Enabled'); - return useMemo( - () => - hasPermission && autoTranslateEnabled - ? { - groups: ['channel', 'group', 'direct', 'direct_multiple', 'team'], - id: 'autotranslate', - title: 'Auto_Translate', - icon: 'language', - template: lazy(() => import('../../../../client/views/room/contextualBar/AutoTranslate')), - order: 20, - full: true, - } - : null, - [autoTranslateEnabled, hasPermission], - ); -}); diff --git a/apps/meteor/app/autotranslate/server/autotranslate.ts b/apps/meteor/app/autotranslate/server/autotranslate.ts index 10c424ffed08..076c7c52818a 100644 --- a/apps/meteor/app/autotranslate/server/autotranslate.ts +++ b/apps/meteor/app/autotranslate/server/autotranslate.ts @@ -1,6 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import { escapeHTML } from '@rocket.chat/string-helpers'; import type { IMessage, IRoom, @@ -10,13 +7,16 @@ import type { ISupportedLanguage, ITranslationResult, } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import { Messages, Subscriptions } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; -import { settings } from '../../settings/server'; import { callbacks } from '../../../lib/callbacks'; -import { Markdown } from '../../markdown/server'; -import { Logger } from '../../logger/server'; import { isTruthy } from '../../../lib/isTruthy'; +import { Markdown } from '../../markdown/server'; +import { settings } from '../../settings/server'; const translationLogger = new Logger('AutoTranslate'); @@ -158,7 +158,7 @@ export abstract class AutoTranslate { tokenizeEmojis(message: IMessage): IMessage { let count = message.tokens?.length || 0; - message.msg = message.msg.replace(/:[+\w\d]+:/g, function (match) { + message.msg = message.msg.replace(/:[+\w\d]+:/g, (match) => { const token = `{${count++}}`; message.tokens?.push({ token, @@ -178,7 +178,7 @@ export abstract class AutoTranslate { // Support ![alt text](http://image url) and [text](http://link) message.msg = message.msg.replace( new RegExp(`(!?\\[)([^\\]]+)(\\]\\((?:${schemes}):\\/\\/[^\\)]+\\))`, 'gm'), - function (_match, pre, text, post) { + (_match, pre, text, post) => { const pretoken = `{${count++}}`; message.tokens?.push({ token: pretoken, @@ -198,7 +198,7 @@ export abstract class AutoTranslate { // Support message.msg = message.msg.replace( new RegExp(`((?:<|<)(?:${schemes}):\\/\\/[^\\|]+\\|)(.+?)(?=>|>)((?:>|>))`, 'gm'), - function (_match, pre, text, post) { + (_match, pre, text, post) => { const pretoken = `{${count++}}`; message.tokens?.push({ token: pretoken, diff --git a/apps/meteor/app/autotranslate/server/deeplTranslate.ts b/apps/meteor/app/autotranslate/server/deeplTranslate.ts index 73c71b076890..8aaed5c12d28 100644 --- a/apps/meteor/app/autotranslate/server/deeplTranslate.ts +++ b/apps/meteor/app/autotranslate/server/deeplTranslate.ts @@ -2,7 +2,6 @@ * @author Vigneshwaran Odayappan */ -import _ from 'underscore'; import type { IMessage, IDeepLTranslation, @@ -12,11 +11,12 @@ import type { ISupportedLanguage, } from '@rocket.chat/core-typings'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import _ from 'underscore'; -import { TranslationProviderRegistry, AutoTranslate } from './autotranslate'; +import { i18n } from '../../../server/lib/i18n'; import { SystemLogger } from '../../../server/lib/logger/system'; import { settings } from '../../settings/server'; -import { i18n } from '../../../server/lib/i18n'; +import { TranslationProviderRegistry, AutoTranslate } from './autotranslate'; /** * DeepL translation service provider class representation. diff --git a/apps/meteor/app/autotranslate/server/googleTranslate.ts b/apps/meteor/app/autotranslate/server/googleTranslate.ts index 36891fe954c1..0e0b7a205765 100644 --- a/apps/meteor/app/autotranslate/server/googleTranslate.ts +++ b/apps/meteor/app/autotranslate/server/googleTranslate.ts @@ -2,7 +2,6 @@ * @author Vigneshwaran Odayappan */ -import _ from 'underscore'; import type { IMessage, IProviderMetadata, @@ -12,11 +11,12 @@ import type { MessageAttachment, } from '@rocket.chat/core-typings'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import _ from 'underscore'; -import { AutoTranslate, TranslationProviderRegistry } from './autotranslate'; +import { i18n } from '../../../server/lib/i18n'; import { SystemLogger } from '../../../server/lib/logger/system'; import { settings } from '../../settings/server'; -import { i18n } from '../../../server/lib/i18n'; +import { AutoTranslate, TranslationProviderRegistry } from './autotranslate'; /** * Represents google translate class diff --git a/apps/meteor/app/autotranslate/server/index.ts b/apps/meteor/app/autotranslate/server/index.ts index 97b558d7218b..8d76d327670e 100644 --- a/apps/meteor/app/autotranslate/server/index.ts +++ b/apps/meteor/app/autotranslate/server/index.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-duplicates */ /** * This file contains the exported members of the package shall be re-used. * @module AutoTranslate, TranslationProviderRegistry @@ -6,7 +5,6 @@ import { TranslationProviderRegistry } from './autotranslate'; import './permissions'; -import './autotranslate'; import './methods/getSupportedLanguages'; import './methods/saveSettings'; import './methods/translateMessage'; diff --git a/apps/meteor/app/autotranslate/server/logger.ts b/apps/meteor/app/autotranslate/server/logger.ts index 70ca0cbf97b6..9fbd129acd73 100644 --- a/apps/meteor/app/autotranslate/server/logger.ts +++ b/apps/meteor/app/autotranslate/server/logger.ts @@ -1,4 +1,4 @@ -import { Logger } from '../../logger/server'; +import { Logger } from '@rocket.chat/logger'; const logger = new Logger('AutoTranslate'); diff --git a/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts b/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts index c3c68fc74006..30760e854ed1 100644 --- a/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts +++ b/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { TranslationProviderRegistry } from '../autotranslate'; diff --git a/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts b/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts index ba5e308844cd..e0118e8d95a4 100644 --- a/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts +++ b/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ISupportedLanguage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { TranslationProviderRegistry } from '..'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/autotranslate/server/methods/saveSettings.ts b/apps/meteor/app/autotranslate/server/methods/saveSettings.ts index 4afaa508bb7d..1ba5bcdfcd76 100644 --- a/apps/meteor/app/autotranslate/server/methods/saveSettings.ts +++ b/apps/meteor/app/autotranslate/server/methods/saveSettings.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Subscriptions } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/autotranslate/server/methods/translateMessage.ts b/apps/meteor/app/autotranslate/server/methods/translateMessage.ts index 23ac0807696b..d90cad90ce77 100644 --- a/apps/meteor/app/autotranslate/server/methods/translateMessage.ts +++ b/apps/meteor/app/autotranslate/server/methods/translateMessage.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IMessage } from '@rocket.chat/core-typings'; import { Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { TranslationProviderRegistry } from '..'; diff --git a/apps/meteor/app/autotranslate/server/msTranslate.ts b/apps/meteor/app/autotranslate/server/msTranslate.ts index 903804da2abd..3e9c9dbd8a35 100644 --- a/apps/meteor/app/autotranslate/server/msTranslate.ts +++ b/apps/meteor/app/autotranslate/server/msTranslate.ts @@ -2,14 +2,14 @@ * @author Vigneshwaran Odayappan */ -import _ from 'underscore'; import type { IMessage, IProviderMetadata, ISupportedLanguage, ITranslationResult, MessageAttachment } from '@rocket.chat/core-typings'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import _ from 'underscore'; +import { i18n } from '../../../server/lib/i18n'; +import { settings } from '../../settings/server'; import { TranslationProviderRegistry, AutoTranslate } from './autotranslate'; import { msLogger } from './logger'; -import { settings } from '../../settings/server'; -import { i18n } from '../../../server/lib/i18n'; /** * Microsoft translation service provider class representation. diff --git a/apps/meteor/app/autotranslate/server/permissions.ts b/apps/meteor/app/autotranslate/server/permissions.ts index fe4c04092743..7fee3b34a90a 100644 --- a/apps/meteor/app/autotranslate/server/permissions.ts +++ b/apps/meteor/app/autotranslate/server/permissions.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Permissions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; Meteor.startup(async () => { if (!(await Permissions.findOne({ _id: 'auto-translate' }))) { diff --git a/apps/meteor/app/bot-helpers/server/index.ts b/apps/meteor/app/bot-helpers/server/index.ts index 7e3ee61620a9..5e19e1652454 100644 --- a/apps/meteor/app/bot-helpers/server/index.ts +++ b/apps/meteor/app/bot-helpers/server/index.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import type { Filter, FindCursor } from 'mongodb'; import type { IUser } from '@rocket.chat/core-typings'; import { UserStatus } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Rooms, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import type { Filter, FindCursor } from 'mongodb'; -import { settings } from '../../settings/server'; -import { hasRoleAsync } from '../../authorization/server/functions/hasRole'; import { removeUserFromRoomMethod } from '../../../server/methods/removeUserFromRoom'; +import { hasRoleAsync } from '../../authorization/server/functions/hasRole'; import { addUsersToRoomMethod } from '../../lib/server/methods/addUsersToRoom'; +import { settings } from '../../settings/server'; /** * BotHelpers helps bots diff --git a/apps/meteor/app/cas/client/cas_client.ts b/apps/meteor/app/cas/client/cas_client.ts index 81204c6d3854..ea4b3047f6bf 100644 --- a/apps/meteor/app/cas/client/cas_client.ts +++ b/apps/meteor/app/cas/client/cas_client.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; import { Random } from '@rocket.chat/random'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../settings/client'; @@ -40,13 +40,13 @@ const openCenteredPopup = (url: string, width: number, height: number) => { const popup = openCenteredPopup(popupUrl, popupWidth, popupHeight); - const checkPopupOpen = setInterval(function () { + const checkPopupOpen = setInterval(() => { let popupClosed; try { // Fix for #328 - added a second test criteria (popup.closed === undefined) // to humour this Android quirk: // http://code.google.com/p/android/issues/detail?id=21061 - popupClosed = popup?.closed === undefined; + popupClosed = popup?.closed || popup?.closed === undefined; } catch (e) { // For some unknown reason, IE9 (and others?) sometimes (when // the popup closes too quickly?) throws "SCRIPT16386: No such diff --git a/apps/meteor/app/cas/server/cas_rocketchat.js b/apps/meteor/app/cas/server/cas_rocketchat.js index 2eeef311859e..f0b62b6ccb8a 100644 --- a/apps/meteor/app/cas/server/cas_rocketchat.js +++ b/apps/meteor/app/cas/server/cas_rocketchat.js @@ -1,6 +1,6 @@ +import { Logger } from '@rocket.chat/logger'; import { ServiceConfiguration } from 'meteor/service-configuration'; -import { Logger } from '../../logger/server'; import { settings } from '../../settings/server'; export const logger = new Logger('CAS'); @@ -12,7 +12,7 @@ async function updateServices(/* record*/) { clearTimeout(timer); } - timer = setTimeout(async function () { + timer = setTimeout(async () => { const data = { // These will pe passed to 'node-cas' as options enabled: settings.get('CAS_enabled'), diff --git a/apps/meteor/app/cas/server/cas_server.js b/apps/meteor/app/cas/server/cas_server.js index 230de6027cb4..25d3b9fdd698 100644 --- a/apps/meteor/app/cas/server/cas_server.js +++ b/apps/meteor/app/cas/server/cas_server.js @@ -1,17 +1,17 @@ import url from 'url'; -import { Meteor } from 'meteor/meteor'; +import { validate } from '@rocket.chat/cas-validate'; +import { CredentialTokens, Rooms, Users } from '@rocket.chat/models'; import { Accounts } from 'meteor/accounts-base'; -import { WebApp } from 'meteor/webapp'; +import { Meteor } from 'meteor/meteor'; import { RoutePolicy } from 'meteor/routepolicy'; +import { WebApp } from 'meteor/webapp'; import _ from 'underscore'; -import { CredentialTokens, Rooms, Users } from '@rocket.chat/models'; -import { validate } from '@rocket.chat/cas-validate'; -import { logger } from './cas_rocketchat'; -import { settings } from '../../settings/server'; -import { _setRealName } from '../../lib/server'; import { createRoom } from '../../lib/server/functions/createRoom'; +import { _setRealName } from '../../lib/server/functions/setRealName'; +import { settings } from '../../settings/server'; +import { logger } from './cas_rocketchat'; RoutePolicy.declare('/_cas/', 'network'); @@ -43,7 +43,7 @@ const casTicket = function (req, token, callback) { service: `${appUrl}/_cas/${token}`, }, ticketId, - async function (err, status, username, details) { + async (err, status, username, details) => { if (err) { logger.error(`error when trying to validate: ${err.message}`); } else if (status) { @@ -87,7 +87,7 @@ const middleware = function (req, res, next) { } // validate ticket - casTicket(req, credentialToken, function () { + casTicket(req, credentialToken, () => { closePopup(res); }); } catch (err) { @@ -97,7 +97,7 @@ const middleware = function (req, res, next) { }; // Listen to incoming OAuth http requests -WebApp.connectHandlers.use(function (req, res, next) { +WebApp.connectHandlers.use((req, res, next) => { middleware(req, res, next); }); @@ -106,7 +106,7 @@ WebApp.connectHandlers.use(function (req, res, next) { * It is call after Accounts.callLoginMethod() is call from client. * */ -Accounts.registerLoginHandler('cas', async function (options) { +Accounts.registerLoginHandler('cas', async (options) => { if (!options.cas) { return undefined; } @@ -141,7 +141,7 @@ Accounts.registerLoginHandler('cas', async function (options) { // Import response attributes if (cas_version >= 2.0) { // Clean & import external attributes - _.each(result.attributes, function (value, ext_name) { + _.each(result.attributes, (value, ext_name) => { if (value) { ext_attrs[ext_name] = value[0]; } @@ -154,11 +154,11 @@ Accounts.registerLoginHandler('cas', async function (options) { // Spoken: Source this internal attribute from these external attributes const attr_map = JSON.parse(syncUserDataFieldMap); - _.each(attr_map, function (source, int_name) { + _.each(attr_map, (source, int_name) => { // Source is our String to interpolate if (source && typeof source.valueOf() === 'string') { let replacedValue = source; - _.each(ext_attrs, function (value, ext_name) { + _.each(ext_attrs, (value, ext_name) => { replacedValue = replacedValue.replace(`%${ext_name}%`, ext_attrs[ext_name]); }); diff --git a/apps/meteor/app/channel-settings/client/index.ts b/apps/meteor/app/channel-settings/client/index.ts deleted file mode 100644 index cd95da0a7d92..000000000000 --- a/apps/meteor/app/channel-settings/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './tabBar'; diff --git a/apps/meteor/app/channel-settings/client/tabBar.ts b/apps/meteor/app/channel-settings/client/tabBar.ts deleted file mode 100644 index ffb431ec39b4..000000000000 --- a/apps/meteor/app/channel-settings/client/tabBar.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { FC, LazyExoticComponent } from 'react'; -import { lazy } from 'react'; - -import { addAction } from '../../../client/views/room/lib/Toolbox'; - -addAction('channel-settings', { - groups: ['channel', 'group'], - id: 'channel-settings', - anonymous: true, - full: true, - title: 'Room_Info', - icon: 'info-circled', - template: lazy(() => import('../../../client/views/room/contextualBar/Info')) as LazyExoticComponent, - order: 1, -}); diff --git a/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.ts b/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.ts index ca360c71d214..f9edd756c771 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveReactWhenReadOnly.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Rooms } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; +import { Rooms } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; export const saveReactWhenReadOnly = async function ( rid: string, diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.ts index affbd82a2083..dae66fcf0acf 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomAnnouncement.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Rooms } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { UpdateResult } from 'mongodb'; export const saveRoomAnnouncement = async function ( diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.ts index 8e5cf0536efb..55d40cf3d7e6 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomCustomFields.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; import { Rooms, Subscriptions } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { UpdateResult } from 'mongodb'; export const saveRoomCustomFields = async function (rid: string, roomCustomFields: Record): Promise { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.ts index 5deb055b6561..2d30ecbd6039 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomDescription.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Rooms } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { UpdateResult } from 'mongodb'; export const saveRoomDescription = async function (rid: string, roomDescription: string, user: IUser): Promise { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts index 8e3847ff8dbd..dc57307b1c4c 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import type { UpdateResult } from 'mongodb'; +import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { isRegisterUser } from '@rocket.chat/core-typings'; import { Rooms } from '@rocket.chat/models'; -import { Message } from '@rocket.chat/core-services'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import type { UpdateResult } from 'mongodb'; export const saveRoomEncrypted = async function (rid: string, encrypted: boolean, user: IUser, sendMessage = true): Promise { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts index 864475a0d229..acbcb4c87c37 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { Integrations, Rooms, Subscriptions } from '@rocket.chat/models'; +import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { Message } from '@rocket.chat/core-services'; +import { Integrations, Rooms, Subscriptions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import type { Document, UpdateResult } from 'mongodb'; -import { getValidRoomName } from '../../../utils/server'; import { callbacks } from '../../../../lib/callbacks'; -import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; +import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; +import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; const updateFName = async (rid: string, displayName: string): Promise<(UpdateResult | Document)[]> => { return Promise.all([Rooms.setFnameById(rid, displayName), Subscriptions.updateFnameByRoomId(rid, displayName)]); diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.ts index ed1a67bccd21..9de6031e0b82 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomReadOnly.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Rooms } from '@rocket.chat/models'; -import type { IUser } from '@rocket.chat/core-typings'; import { Message } from '@rocket.chat/core-services'; +import type { IUser } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; export async function saveRoomReadOnly( rid: string, diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.ts index 64ee9d0d73a4..6ebd643afeb0 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomSystemMessages.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; import type { MessageTypesValues } from '@rocket.chat/core-typings'; import { Rooms } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { MessageTypesValues as messageTypesValues } from '../../../lib/lib/MessageTypes'; diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.ts index 2db467582029..11b9b5b6e565 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomTopic.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Rooms } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; +import { Rooms } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomType.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomType.ts index 5001f784921c..e8a60d1ea0eb 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomType.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomType.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Rooms, Subscriptions } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; import type { IRoom, IUser } from '@rocket.chat/core-typings'; +import { Rooms, Subscriptions } from '@rocket.chat/models'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { UpdateResult, Document } from 'mongodb'; -import { settings } from '../../../settings/server'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; import { i18n } from '../../../../server/lib/i18n'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; +import { settings } from '../../../settings/server'; export const saveRoomType = async function ( rid: string, diff --git a/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.ts b/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.ts index 073b36c3a0c6..aee596402cc6 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveStreamingOptions.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { Rooms } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; export const saveStreamingOptions = async function (rid: string, options: Record): Promise { if (!Match.test(rid, String)) { diff --git a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts index 77f1b1b536c1..b1845511c7b7 100644 --- a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts +++ b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts @@ -1,26 +1,26 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; +import { Team } from '@rocket.chat/core-services'; import type { IRoom, IRoomWithRetentionPolicy, IUser, MessageTypesValues } from '@rocket.chat/core-typings'; import { TEAM_TYPE } from '@rocket.chat/core-typings'; -import { Team } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Rooms, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { setRoomAvatar } from '../../../lib/server/functions/setRoomAvatar'; +import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { saveRoomName } from '../functions/saveRoomName'; -import { saveRoomTopic } from '../functions/saveRoomTopic'; +import { setRoomAvatar } from '../../../lib/server/functions/setRoomAvatar'; +import { saveReactWhenReadOnly } from '../functions/saveReactWhenReadOnly'; import { saveRoomAnnouncement } from '../functions/saveRoomAnnouncement'; import { saveRoomCustomFields } from '../functions/saveRoomCustomFields'; import { saveRoomDescription } from '../functions/saveRoomDescription'; -import { saveRoomType } from '../functions/saveRoomType'; +import { saveRoomEncrypted } from '../functions/saveRoomEncrypted'; +import { saveRoomName } from '../functions/saveRoomName'; import { saveRoomReadOnly } from '../functions/saveRoomReadOnly'; -import { saveReactWhenReadOnly } from '../functions/saveReactWhenReadOnly'; import { saveRoomSystemMessages } from '../functions/saveRoomSystemMessages'; -import { saveRoomEncrypted } from '../functions/saveRoomEncrypted'; +import { saveRoomTopic } from '../functions/saveRoomTopic'; +import { saveRoomType } from '../functions/saveRoomType'; import { saveStreamingOptions } from '../functions/saveStreamingOptions'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; type RoomSettings = { roomAvatar: string; diff --git a/apps/meteor/app/cloud/server/functions/connectWorkspace.ts b/apps/meteor/app/cloud/server/functions/connectWorkspace.ts index 263b89b952ed..3c8a587fa558 100644 --- a/apps/meteor/app/cloud/server/functions/connectWorkspace.ts +++ b/apps/meteor/app/cloud/server/functions/connectWorkspace.ts @@ -1,11 +1,11 @@ import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; import { getRedirectUri } from './getRedirectUri'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { settings } from '../../../settings/server'; import { saveRegistrationData } from './saveRegistrationData'; -import { SystemLogger } from '../../../../server/lib/logger/system'; export async function connectWorkspace(token: string) { const { connectToCloud } = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.ts b/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.ts index f5cfe9b208af..780aa5c67a99 100644 --- a/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.ts +++ b/apps/meteor/app/cloud/server/functions/finishOAuthAuthorization.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; import { Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { Meteor } from 'meteor/meteor'; -import { getRedirectUri } from './getRedirectUri'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import { userScopes } from '../oauthScopes'; -import { SystemLogger } from '../../../../server/lib/logger/system'; +import { getRedirectUri } from './getRedirectUri'; export async function finishOAuthAuthorization(code: string, state: string) { if (settings.get('Cloud_Workspace_Registration_State') !== state) { diff --git a/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts b/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts index def43e8c16d8..4a35c9834ba5 100644 --- a/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts +++ b/apps/meteor/app/cloud/server/functions/getConfirmationPoll.ts @@ -1,8 +1,8 @@ import type { CloudConfirmationPollData } from '@rocket.chat/core-typings'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; export async function getConfirmationPoll(deviceCode: string): Promise { const cloudUrl = settings.get('Cloud_Url'); diff --git a/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts b/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts index e44a772974a5..14cb2f4a57ce 100644 --- a/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts +++ b/apps/meteor/app/cloud/server/functions/getOAuthAuthorizationUrl.ts @@ -1,9 +1,9 @@ -import { Random } from '@rocket.chat/random'; import { Settings } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; -import { getRedirectUri } from './getRedirectUri'; import { settings } from '../../../settings/server'; import { userScopes } from '../oauthScopes'; +import { getRedirectUri } from './getRedirectUri'; export async function getOAuthAuthorizationUrl() { const state = Random.id(); diff --git a/apps/meteor/app/cloud/server/functions/getUserCloudAccessToken.ts b/apps/meteor/app/cloud/server/functions/getUserCloudAccessToken.ts index 4617f97d744a..d7d87417e349 100644 --- a/apps/meteor/app/cloud/server/functions/getUserCloudAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getUserCloudAccessToken.ts @@ -1,14 +1,14 @@ -import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; +import { userScopes } from '../oauthScopes'; import { getRedirectUri } from './getRedirectUri'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { removeWorkspaceRegistrationInfo } from './removeWorkspaceRegistrationInfo'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { userLoggedOut } from './userLoggedOut'; -import { settings } from '../../../settings/server'; -import { userScopes } from '../oauthScopes'; -import { SystemLogger } from '../../../../server/lib/logger/system'; export async function getUserCloudAccessToken(userId: string, forceNew = false, scope = '', save = true) { const { connectToCloud, workspaceRegistered } = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 5a0e69058738..1a69d108ae4c 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -1,8 +1,8 @@ import { Settings } from '@rocket.chat/models'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; import { settings } from '../../../settings/server'; +import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; /** * @param {boolean} forceNew diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts index 7a21b5e5d719..4a0c4b5fe394 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts @@ -1,11 +1,11 @@ import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { getRedirectUri } from './getRedirectUri'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { removeWorkspaceRegistrationInfo } from './removeWorkspaceRegistrationInfo'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import { workspaceScopes } from '../oauthScopes'; -import { SystemLogger } from '../../../../server/lib/logger/system'; +import { getRedirectUri } from './getRedirectUri'; +import { removeWorkspaceRegistrationInfo } from './removeWorkspaceRegistrationInfo'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; export async function getWorkspaceAccessTokenWithScope(scope = '') { const { connectToCloud, workspaceRegistered } = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts index 38e6ea372556..f3b6dfc4238a 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts @@ -1,5 +1,5 @@ -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { settings } from '../../../settings/server'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; export async function getWorkspaceKey() { const { connectToCloud, workspaceRegistered } = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts index 9e2774be2fb7..275e646e5343 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts @@ -1,11 +1,11 @@ import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { LICENSE_VERSION } from '../license'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; +import { LICENSE_VERSION } from '../license'; +import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; export async function getWorkspaceLicense(): Promise<{ updated: boolean; license: string }> { const currentLicense = await Settings.findOne('Cloud_Workspace_License'); diff --git a/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts b/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts index 06707e34618a..2a04aa54cfe7 100644 --- a/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts +++ b/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts @@ -2,9 +2,9 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; -import { SystemLogger } from '../../../../server/lib/logger/system'; export async function registerPreIntentWorkspaceWizard(): Promise { const firstUser = (await Users.getOldest({ projection: { name: 1, emails: 1 } })) as IUser | undefined; diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts index c7351a28b06c..8e1e03113af4 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import { Settings } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts index 9ff29a5bd48f..de9fafc99065 100644 --- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts +++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspace.ts @@ -1,11 +1,11 @@ import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { syncWorkspace } from './syncWorkspace'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; -import { SystemLogger } from '../../../../server/lib/logger/system'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; +import { syncWorkspace } from './syncWorkspace'; export async function startRegisterWorkspace(resend = false) { const { workspaceRegistered, connectToCloud } = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts b/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts index 50c4285bc2ab..3afe84c409ec 100644 --- a/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts +++ b/apps/meteor/app/cloud/server/functions/startRegisterWorkspaceSetupWizard.ts @@ -1,9 +1,9 @@ import type { CloudRegistrationIntentData } from '@rocket.chat/core-typings'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; -import { SystemLogger } from '../../../../server/lib/logger/system'; export async function startRegisterWorkspaceSetupWizard(resend = false, email: string): Promise { const regInfo = await buildWorkspaceRegistrationData(email); diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace.ts index 5a59ea463d00..9337fd0a0172 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace.ts @@ -1,14 +1,14 @@ -import { Settings } from '@rocket.chat/models'; import { NPS, Banner } from '@rocket.chat/core-services'; +import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { getAndCreateNpsSurvey } from '../../../../server/services/nps/getAndCreateNpsSurvey'; +import { settings } from '../../../settings/server'; import { buildWorkspaceRegistrationData } from './buildRegistrationData'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { getWorkspaceLicense } from './getWorkspaceLicense'; -import { settings } from '../../../settings/server'; -import { getAndCreateNpsSurvey } from '../../../../server/services/nps/getAndCreateNpsSurvey'; -import { SystemLogger } from '../../../../server/lib/logger/system'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; export async function syncWorkspace(reconnectCheck = false) { const { workspaceRegistered, connectToCloud } = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/cloud/server/functions/userLogout.ts b/apps/meteor/app/cloud/server/functions/userLogout.ts index 9630e13b789d..e03f96df679d 100644 --- a/apps/meteor/app/cloud/server/functions/userLogout.ts +++ b/apps/meteor/app/cloud/server/functions/userLogout.ts @@ -1,11 +1,10 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import { Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { userLoggedOut } from './userLoggedOut'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; +import { userLoggedOut } from './userLoggedOut'; export async function userLogout(userId: string): Promise { const { connectToCloud, workspaceRegistered } = await retrieveRegistrationStatus(); @@ -22,21 +21,21 @@ export async function userLogout(userId: string): Promise { if (user?.services?.cloud?.refreshToken) { try { - const client_id = settings.get('Cloud_Workspace_Client_Id'); - if (!client_id) { + const clientId = settings.get('Cloud_Workspace_Client_Id'); + if (!clientId) { return ''; } const cloudUrl = settings.get('Cloud_Url'); - const client_secret = settings.get('Cloud_Workspace_Client_Secret'); + const clientSecret = settings.get('Cloud_Workspace_Client_Secret'); const { refreshToken } = user.services.cloud; await fetch(`${cloudUrl}/api/oauth/revoke`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, params: { - client_id, - client_secret, + client_id: clientId, + client_secret: clientSecret, token: refreshToken, token_type_hint: 'refresh_token', }, diff --git a/apps/meteor/app/cloud/server/index.ts b/apps/meteor/app/cloud/server/index.ts index 7f63e03c1017..4bb1f634978e 100644 --- a/apps/meteor/app/cloud/server/index.ts +++ b/apps/meteor/app/cloud/server/index.ts @@ -1,18 +1,18 @@ -import { Meteor } from 'meteor/meteor'; import { cronJobs } from '@rocket.chat/cron'; +import { Meteor } from 'meteor/meteor'; +import { SystemLogger } from '../../../server/lib/logger/system'; +import { settings } from '../../settings/server'; +import { connectWorkspace } from './functions/connectWorkspace'; import { getWorkspaceAccessToken } from './functions/getWorkspaceAccessToken'; import { getWorkspaceAccessTokenWithScope } from './functions/getWorkspaceAccessTokenWithScope'; import { retrieveRegistrationStatus } from './functions/retrieveRegistrationStatus'; import { syncWorkspace } from './functions/syncWorkspace'; -import { connectWorkspace } from './functions/connectWorkspace'; -import { settings } from '../../settings/server'; -import { SystemLogger } from '../../../server/lib/logger/system'; import './methods'; const licenseCronName = 'Cloud Workspace Sync'; -Meteor.startup(async function () { +Meteor.startup(async () => { // run token/license sync if registered let TroubleshootDisableWorkspaceSync: boolean; settings.watch('Troubleshoot_Disable_Workspace_Sync', async (value) => { diff --git a/apps/meteor/app/cloud/server/methods.ts b/apps/meteor/app/cloud/server/methods.ts index 797ed964c171..d2fbac1af881 100644 --- a/apps/meteor/app/cloud/server/methods.ts +++ b/apps/meteor/app/cloud/server/methods.ts @@ -1,19 +1,19 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { retrieveRegistrationStatus } from './functions/retrieveRegistrationStatus'; +import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; +import { buildWorkspaceRegistrationData } from './functions/buildRegistrationData'; +import { checkUserHasCloudLogin } from './functions/checkUserHasCloudLogin'; import { connectWorkspace } from './functions/connectWorkspace'; -import { reconnectWorkspace } from './functions/reconnectWorkspace'; -import { getOAuthAuthorizationUrl } from './functions/getOAuthAuthorizationUrl'; +import { disconnectWorkspace } from './functions/disconnectWorkspace'; import { finishOAuthAuthorization } from './functions/finishOAuthAuthorization'; +import { getOAuthAuthorizationUrl } from './functions/getOAuthAuthorizationUrl'; +import { reconnectWorkspace } from './functions/reconnectWorkspace'; +import { retrieveRegistrationStatus } from './functions/retrieveRegistrationStatus'; import { startRegisterWorkspace } from './functions/startRegisterWorkspace'; -import { disconnectWorkspace } from './functions/disconnectWorkspace'; import { syncWorkspace } from './functions/syncWorkspace'; -import { checkUserHasCloudLogin } from './functions/checkUserHasCloudLogin'; import { userLogout } from './functions/userLogout'; -import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; -import { buildWorkspaceRegistrationData } from './functions/buildRegistrationData'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -40,6 +40,10 @@ declare module '@rocket.chat/ui-contexts' { } Meteor.methods({ + /** + * @deprecated this method is deprecated and will be removed soon. + * Prefer using cloud.registrationStatus rest api. + */ async 'cloud:checkRegisterStatus'() { const uid = Meteor.userId(); diff --git a/apps/meteor/app/cors/client/index.ts b/apps/meteor/app/cors/client/index.ts index 288bd2299245..3c48eb176f46 100644 --- a/apps/meteor/app/cors/client/index.ts +++ b/apps/meteor/app/cors/client/index.ts @@ -3,8 +3,8 @@ import { Tracker } from 'meteor/tracker'; import { settings } from '../../settings/client'; -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { Meteor.absoluteUrl.defaultOptions.secure = Boolean(settings.get('Force_SSL')); }); }); diff --git a/apps/meteor/app/cors/server/cors.ts b/apps/meteor/app/cors/server/cors.ts index a3a585585fe4..03a42e45a17b 100644 --- a/apps/meteor/app/cors/server/cors.ts +++ b/apps/meteor/app/cors/server/cors.ts @@ -1,13 +1,13 @@ -import url from 'url'; import type http from 'http'; +import url from 'url'; +import { Logger } from '@rocket.chat/logger'; import { Meteor } from 'meteor/meteor'; import type { StaticFiles } from 'meteor/webapp'; import { WebApp, WebAppInternals } from 'meteor/webapp'; import _ from 'underscore'; import { settings } from '../../settings/server'; -import { Logger } from '../../logger/server'; // Taken from 'connect' types type NextFunction = (err?: any) => void; @@ -21,7 +21,7 @@ settings.watch( }), ); -WebApp.rawConnectHandlers.use(function (_req: http.IncomingMessage, res: http.ServerResponse, next: NextFunction) { +WebApp.rawConnectHandlers.use((_req: http.IncomingMessage, res: http.ServerResponse, next: NextFunction) => { // XSS Protection for old browsers (IE) res.setHeader('X-XSS-Protection', '1'); @@ -93,7 +93,7 @@ const oldHttpServerListeners = WebApp.httpServer.listeners('request').slice(0); WebApp.httpServer.removeAllListeners('request'); -WebApp.httpServer.addListener('request', function (req, res, ...args) { +WebApp.httpServer.addListener('request', (req, res, ...args) => { const next = () => { for (const oldListener of oldHttpServerListeners) { oldListener.apply(WebApp.httpServer, [req, res, ...args]); diff --git a/apps/meteor/app/cors/server/index.ts b/apps/meteor/app/cors/server/index.ts index f8805e23bc36..e91bda0591a5 100644 --- a/apps/meteor/app/cors/server/index.ts +++ b/apps/meteor/app/cors/server/index.ts @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor'; import { settings } from '../../settings/server'; -Meteor.startup(function () { +Meteor.startup(() => { settings.watch('Force_SSL', (value) => { Meteor.absoluteUrl.defaultOptions.secure = Boolean(value); }); diff --git a/apps/meteor/app/crowd/client/loginHelper.js b/apps/meteor/app/crowd/client/loginHelper.js index fad5d58cff49..a2bb14023b3a 100644 --- a/apps/meteor/app/crowd/client/loginHelper.js +++ b/apps/meteor/app/crowd/client/loginHelper.js @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; Meteor.loginWithCrowd = function (...args) { // Pull username and password diff --git a/apps/meteor/app/crowd/server/crowd.ts b/apps/meteor/app/crowd/server/crowd.ts index b998abc631b9..b6b94f33e566 100644 --- a/apps/meteor/app/crowd/server/crowd.ts +++ b/apps/meteor/app/crowd/server/crowd.ts @@ -1,16 +1,17 @@ -import { Meteor } from 'meteor/meteor'; -import { SHA256 } from '@rocket.chat/sha256'; -import { Accounts } from 'meteor/accounts-base'; -import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; import { cronJobs } from '@rocket.chat/cron'; +import { Users } from '@rocket.chat/models'; +import { SHA256 } from '@rocket.chat/sha256'; +import AtlassianCrowd from 'atlassian-crowd-patched'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { _setRealName } from '../../lib/server'; -import { settings } from '../../settings/server'; -import { deleteUser } from '../../lib/server/functions'; +import { crowdIntervalValuesToCronMap } from '../../../server/settings/crowd'; +import { deleteUser } from '../../lib/server/functions/deleteUser'; +import { _setRealName } from '../../lib/server/functions/setRealName'; import { setUserActiveStatus } from '../../lib/server/functions/setUserActiveStatus'; +import { settings } from '../../settings/server'; import { logger } from './logger'; -import { crowdIntervalValuesToCronMap } from '../../../server/settings/crowd'; type CrowdUser = Pick & { crowd: Record; crowd_username: string }; @@ -51,8 +52,6 @@ export class CROWD { }; constructor() { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const AtlassianCrowd = require('atlassian-crowd-patched'); let url = settings.get('CROWD_URL'); this.options = { @@ -225,7 +224,7 @@ export class CROWD { logger.warn('Could not find user in CROWD with username or email:', crowdUsername, email); if (settings.get('CROWD_Remove_Orphaned_Users') === true) { logger.info('Removing user:', crowdUsername); - setImmediate(async function () { + setImmediate(async () => { await deleteUser(user._id); logger.info('User removed:', crowdUsername); }); diff --git a/apps/meteor/app/crowd/server/logger.ts b/apps/meteor/app/crowd/server/logger.ts index 6dfa29c4095b..b241746e294b 100644 --- a/apps/meteor/app/crowd/server/logger.ts +++ b/apps/meteor/app/crowd/server/logger.ts @@ -1,3 +1,3 @@ -import { Logger } from '../../logger/server'; +import { Logger } from '@rocket.chat/logger'; export const logger = new Logger('CROWD'); diff --git a/apps/meteor/app/crowd/server/methods.ts b/apps/meteor/app/crowd/server/methods.ts index 3f84e83cbd38..758ebd1fcb3c 100644 --- a/apps/meteor/app/crowd/server/methods.ts +++ b/apps/meteor/app/crowd/server/methods.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; -import { logger } from './logger'; +import { settings } from '../../settings/server'; import { CROWD } from './crowd'; +import { logger } from './logger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/custom-oauth/client/custom_oauth_client.js b/apps/meteor/app/custom-oauth/client/custom_oauth_client.js index dda7d86e6349..a11277758438 100644 --- a/apps/meteor/app/custom-oauth/client/custom_oauth_client.js +++ b/apps/meteor/app/custom-oauth/client/custom_oauth_client.js @@ -1,10 +1,10 @@ +import { Random } from '@rocket.chat/random'; import { capitalize } from '@rocket.chat/string-helpers'; -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; import { Accounts } from 'meteor/accounts-base'; -import { Random } from '@rocket.chat/random'; -import { ServiceConfiguration } from 'meteor/service-configuration'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { OAuth } from 'meteor/oauth'; +import { ServiceConfiguration } from 'meteor/service-configuration'; import './swapSessionStorage'; import { isURL } from '../../../lib/utils/isURL'; diff --git a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js index 4bb0b7000602..bb939febaef8 100644 --- a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js +++ b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js @@ -1,19 +1,19 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; +import { LDAP } from '@rocket.chat/core-services'; +import { Logger } from '@rocket.chat/logger'; +import { Users } from '@rocket.chat/models'; +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Accounts } from 'meteor/accounts-base'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { OAuth } from 'meteor/oauth'; import { ServiceConfiguration } from 'meteor/service-configuration'; import _ from 'underscore'; -import { LDAP } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { normalizers, fromTemplate, renameInvalidProperties } from './transform_helpers'; -import { Logger } from '../../logger/server'; +import { callbacks } from '../../../lib/callbacks'; import { isURL } from '../../../lib/utils/isURL'; import { registerAccessTokenService } from '../../lib/server/oauth/oauth'; -import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; +import { normalizers, fromTemplate, renameInvalidProperties } from './transform_helpers'; const logger = new Logger('CustomOAuth'); @@ -402,7 +402,7 @@ export class CustomOAuth { const self = this; const whitelisted = ['id', 'email', 'username', 'name', this.rolesClaim]; - registerAccessTokenService(name, async function (options) { + registerAccessTokenService(name, async (options) => { check( options, Match.ObjectIncluding({ diff --git a/apps/meteor/app/custom-sounds/client/index.ts b/apps/meteor/app/custom-sounds/client/index.ts index 77706dca50ec..d1154824c78c 100644 --- a/apps/meteor/app/custom-sounds/client/index.ts +++ b/apps/meteor/app/custom-sounds/client/index.ts @@ -1,4 +1,21 @@ -import './notifications/deleteCustomSound'; -import './notifications/updateCustomSound'; +import { Meteor } from 'meteor/meteor'; +import { Notifications } from '../../notifications/client'; +import { CachedCollectionManager } from '../../ui-cached-collection/client'; +import { CustomSounds } from './lib/CustomSounds'; + +Meteor.startup(() => { + CachedCollectionManager.onLogin(() => { + Notifications.onAll('public-info', ([key, data]) => { + switch (key) { + case 'updateCustomSound': + CustomSounds.update(data[0].soundData); + break; + case 'deleteCustomSound': + CustomSounds.remove(data[0].soundData); + break; + } + }); + }); +}); export { CustomSounds } from './lib/CustomSounds'; diff --git a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts index f35efc6680ee..a4f59136a1f9 100644 --- a/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts +++ b/apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts @@ -1,6 +1,6 @@ +import type { ICustomSound } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; -import type { ICustomSound } from '@rocket.chat/core-typings'; import { CachedCollectionManager } from '../../../ui-cached-collection/client'; import { getURL } from '../../../utils/client'; @@ -42,7 +42,7 @@ class CustomSoundsClass { const audio = document.createElement('audio'); audio.id = getCustomSoundId(sound._id); - audio.preload = 'auto'; + audio.preload = 'none'; audio.appendChild(source); document.body.appendChild(audio); diff --git a/apps/meteor/app/custom-sounds/client/notifications/deleteCustomSound.js b/apps/meteor/app/custom-sounds/client/notifications/deleteCustomSound.js deleted file mode 100644 index b88ba3ead3e6..000000000000 --- a/apps/meteor/app/custom-sounds/client/notifications/deleteCustomSound.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { CachedCollectionManager } from '../../../ui-cached-collection/client'; -import { Notifications } from '../../../notifications/client'; -import { CustomSounds } from '../lib/CustomSounds'; - -Meteor.startup(() => - CachedCollectionManager.onLogin(() => Notifications.onAll('deleteCustomSound', (data) => CustomSounds.remove(data.soundData))), -); diff --git a/apps/meteor/app/custom-sounds/client/notifications/updateCustomSound.js b/apps/meteor/app/custom-sounds/client/notifications/updateCustomSound.js deleted file mode 100644 index b6026635f1b7..000000000000 --- a/apps/meteor/app/custom-sounds/client/notifications/updateCustomSound.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { CachedCollectionManager } from '../../../ui-cached-collection/client'; -import { Notifications } from '../../../notifications/client'; -import { CustomSounds } from '../lib/CustomSounds'; - -Meteor.startup(() => - CachedCollectionManager.onLogin(() => Notifications.onAll('updateCustomSound', (data) => CustomSounds.update(data.soundData))), -); diff --git a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts index eab7570c6684..5ddf0cc66e52 100644 --- a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts +++ b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { CustomSounds } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; import type { ICustomSound } from '@rocket.chat/core-typings'; +import { CustomSounds } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds'; diff --git a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts index 56f061fce83b..4b48a64dc14c 100644 --- a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts +++ b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { CustomSounds } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; +import { CustomSounds } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds'; diff --git a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts index b53d090282e3..2c2130fc0d30 100644 --- a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts +++ b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import type { ICustomSound } from '@rocket.chat/core-typings'; import { CustomSounds } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { ICustomSound } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts b/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts index 7712f6bc56f9..eee693634d7d 100644 --- a/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts +++ b/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import { api } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { RequiredField } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFile } from '../../../file/server'; diff --git a/apps/meteor/app/custom-sounds/server/startup/custom-sounds.js b/apps/meteor/app/custom-sounds/server/startup/custom-sounds.js index be36a5cd267e..87d13f1deffd 100644 --- a/apps/meteor/app/custom-sounds/server/startup/custom-sounds.js +++ b/apps/meteor/app/custom-sounds/server/startup/custom-sounds.js @@ -1,13 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { RocketChatFile } from '../../../file/server'; import { settings } from '../../../settings/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; export let RocketChatFileCustomSoundsInstance; -Meteor.startup(function () { +Meteor.startup(() => { let storeType = 'GridFS'; if (settings.get('CustomSounds_Storage_Type')) { @@ -34,7 +34,7 @@ Meteor.startup(function () { absolutePath: path, }); - return WebApp.connectHandlers.use('/custom-sounds/', async function (req, res /* , next*/) { + return WebApp.connectHandlers.use('/custom-sounds/', async (req, res /* , next*/) => { const fileId = decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, '')); if (!fileId) { diff --git a/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts b/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts index 785a2114b047..5eb2ef38e5b7 100644 --- a/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts +++ b/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts @@ -1,15 +1,15 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../settings/client'; -import { hasPermission } from '../../authorization/client'; -import { MessageAction } from '../../ui-utils/client'; -import { messageArgs } from '../../../client/lib/utils/messageArgs'; -import { imperativeModal } from '../../../client/lib/imperativeModal'; import CreateDiscussion from '../../../client/components/CreateDiscussion/CreateDiscussion'; +import { imperativeModal } from '../../../client/lib/imperativeModal'; import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; +import { messageArgs } from '../../../client/lib/utils/messageArgs'; +import { hasPermission } from '../../authorization/client'; +import { settings } from '../../settings/client'; +import { MessageAction } from '../../ui-utils/client'; -Meteor.startup(function () { +Meteor.startup(() => { Tracker.autorun(() => { if (!settings.get('Discussion_enabled')) { return MessageAction.removeButton('start-discussion'); @@ -19,7 +19,8 @@ Meteor.startup(function () { id: 'start-discussion', icon: 'discussion', label: 'Discussion_start', - context: ['message', 'message-mobile'], + type: 'communication', + context: ['message', 'message-mobile', 'videoconf'], async action(_, props) { const { message = messageArgs(this).msg, room } = props; diff --git a/apps/meteor/app/discussion/client/index.ts b/apps/meteor/app/discussion/client/index.ts index f45e6458ae7a..62e11191b493 100644 --- a/apps/meteor/app/discussion/client/index.ts +++ b/apps/meteor/app/discussion/client/index.ts @@ -1,4 +1,3 @@ // Other UI extensions import './lib/messageTypes/discussionMessage'; import './createDiscussionMessageAction'; -import './tabBar'; diff --git a/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js b/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js index 2ef97320874d..a7f0ef0a1d97 100644 --- a/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js +++ b/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { MessageTypes } from '../../../../ui-utils/client'; -Meteor.startup(function () { +Meteor.startup(() => { MessageTypes.registerType({ id: 'discussion-created', system: false, diff --git a/apps/meteor/app/discussion/client/tabBar.ts b/apps/meteor/app/discussion/client/tabBar.ts deleted file mode 100644 index a9bece626e54..000000000000 --- a/apps/meteor/app/discussion/client/tabBar.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useMemo, lazy } from 'react'; -import { useSetting } from '@rocket.chat/ui-contexts'; -import { isRoomFederated } from '@rocket.chat/core-typings'; - -import { addAction } from '../../../client/views/room/lib/Toolbox'; - -const template = lazy(() => import('../../../client/views/room/contextualBar/Discussions')); - -addAction('discussions', ({ room }) => { - const discussionEnabled = useSetting('Discussion_enabled'); - const federated = isRoomFederated(room); - - return useMemo( - () => - discussionEnabled && !room.prid - ? { - groups: ['channel', 'group', 'direct', 'direct_multiple', 'team'], - id: 'discussions', - title: 'Discussions', - icon: 'discussion', - template, - full: true, - ...(federated && { - 'disabled': true, - 'data-tooltip': 'Discussions_unavailable_for_federation', - }), - order: 3, - } - : null, - [discussionEnabled, room.prid, federated], - ); -}); diff --git a/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts b/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts index 73ad09a6fcf6..b6054b6dcccf 100644 --- a/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts +++ b/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts @@ -1,7 +1,7 @@ import { Messages, Rooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { deleteRoom } from '../../../lib/server'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; /** * We need to propagate the writing of new message in a discussion to the linking @@ -9,7 +9,7 @@ import { deleteRoom } from '../../../lib/server'; */ callbacks.add( 'afterSaveMessage', - async function (message, { _id, prid }) { + async (message, { _id, prid }) => { if (!prid) { return message; } @@ -35,7 +35,7 @@ callbacks.add( callbacks.add( 'afterDeleteMessage', - async function (message, { _id, prid }) { + async (message, { _id, prid }) => { if (prid) { const room = await Rooms.findOneById(_id, { projection: { diff --git a/apps/meteor/app/discussion/server/methods/createDiscussion.ts b/apps/meteor/app/discussion/server/methods/createDiscussion.ts index cd2befbf036c..ce5c09947a60 100644 --- a/apps/meteor/app/discussion/server/methods/createDiscussion.ts +++ b/apps/meteor/app/discussion/server/methods/createDiscussion.ts @@ -1,17 +1,20 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; +import { Message } from '@rocket.chat/core-services'; import type { IMessage, IRoom, IUser, MessageAttachmentDefault } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Messages, Rooms, Users } from '@rocket.chat/models'; -import { Message } from '@rocket.chat/core-services'; +import { Random } from '@rocket.chat/random'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; -import { createRoom, addUserToRoom, sendMessage, attachMessage } from '../../../lib/server'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { i18n } from '../../../../server/lib/i18n'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; +import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; +import { hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; +import { attachMessage } from '../../../lib/server/functions/attachMessage'; +import { createRoom } from '../../../lib/server/functions/createRoom'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; +import { settings } from '../../../settings/server'; const getParentRoom = async (rid: IRoom['_id']) => { const room = await Rooms.findOne(rid); diff --git a/apps/meteor/app/discussion/server/permissions.ts b/apps/meteor/app/discussion/server/permissions.ts index 38fdbc68ab91..db5fe785a975 100644 --- a/apps/meteor/app/discussion/server/permissions.ts +++ b/apps/meteor/app/discussion/server/permissions.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Permissions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; Meteor.startup(async () => { // Add permissions for discussion diff --git a/apps/meteor/app/dolphin/client/lib.ts b/apps/meteor/app/dolphin/client/lib.ts index 9fa11208d3c8..c04ee1b7859d 100644 --- a/apps/meteor/app/dolphin/client/lib.ts +++ b/apps/meteor/app/dolphin/client/lib.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../settings/client'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; +import { settings } from '../../settings/client'; const config = { serverURL: '', @@ -20,7 +20,7 @@ const config = { const Dolphin = new CustomOAuth('dolphin', config); Meteor.startup(() => - Tracker.autorun(function () { + Tracker.autorun(() => { if (settings.get('Accounts_OAuth_Dolphin_URL')) { config.serverURL = settings.get('Accounts_OAuth_Dolphin_URL'); return Dolphin.configure(config); diff --git a/apps/meteor/app/dolphin/server/lib.ts b/apps/meteor/app/dolphin/server/lib.ts index f6693807996e..65a8ca6d2dac 100644 --- a/apps/meteor/app/dolphin/server/lib.ts +++ b/apps/meteor/app/dolphin/server/lib.ts @@ -1,10 +1,11 @@ +import type { IUser } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; -import type { IUser } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/server'; -import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { callbacks } from '../../../lib/callbacks'; +import { beforeCreateUserCallback } from '../../../lib/callbacks/beforeCreateUserCallback'; +import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { settings } from '../../settings/server'; const config = { serverURL: '', @@ -22,6 +23,7 @@ const config = { const Dolphin = new CustomOAuth('dolphin', config); function DolphinOnCreateUser(options: any, user?: IUser) { + // TODO: callbacks Fix this if (user?.services?.dolphin?.NickName) { user.username = user.services.dolphin.NickName; } @@ -48,5 +50,5 @@ Meteor.startup(async () => { await ServiceConfiguration.configurations.upsertAsync({ service: 'dolphin' }, { $set: data }); } - callbacks.add('beforeCreateUser', DolphinOnCreateUser, callbacks.priority.HIGH, 'dolphin'); + beforeCreateUserCallback.add(DolphinOnCreateUser, callbacks.priority.HIGH, 'dolphin'); }); diff --git a/apps/meteor/app/drupal/client/lib.ts b/apps/meteor/app/drupal/client/lib.ts index 89454e43e6bf..9edbb560a450 100644 --- a/apps/meteor/app/drupal/client/lib.ts +++ b/apps/meteor/app/drupal/client/lib.ts @@ -1,9 +1,9 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/client'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; +import { settings } from '../../settings/client'; // Drupal Server CallBack URL needs to be http(s)://{rocketchat.server}[:port]/_oauth/drupal // In RocketChat -> Administration the URL needs to be http(s)://{drupal.server}/ @@ -26,8 +26,8 @@ const config: OauthConfig = { const Drupal = new CustomOAuth('drupal', config); -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { if (settings.get('API_Drupal_URL')) { config.serverURL = settings.get('API_Drupal_URL'); Drupal.configure(config); diff --git a/apps/meteor/app/drupal/server/lib.ts b/apps/meteor/app/drupal/server/lib.ts index ca07a8d73903..d137551fb837 100644 --- a/apps/meteor/app/drupal/server/lib.ts +++ b/apps/meteor/app/drupal/server/lib.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import type { OauthConfig } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { settings } from '../../settings/server'; // Drupal Server CallBack URL needs to be http(s)://{rocketchat.server}[:port]/_oauth/drupal // In RocketChat -> Administration the URL needs to be http(s)://{drupal.server}/ @@ -25,8 +25,8 @@ const config: OauthConfig = { const Drupal = new CustomOAuth('drupal', config); -Meteor.startup(function () { - settings.watch('API_Drupal_URL', function (value) { +Meteor.startup(() => { + settings.watch('API_Drupal_URL', (value) => { config.serverURL = value; Drupal.configure(config); }); diff --git a/apps/meteor/app/e2e/client/helper.js b/apps/meteor/app/e2e/client/helper.js index 8bc3591a7dc2..2e0843ee3380 100644 --- a/apps/meteor/app/e2e/client/helper.js +++ b/apps/meteor/app/e2e/client/helper.js @@ -1,14 +1,14 @@ -/* eslint-disable new-cap, no-proto */ - -import ByteBuffer from 'bytebuffer'; import { Random } from '@rocket.chat/random'; +import ByteBuffer from 'bytebuffer'; +// eslint-disable-next-line no-proto const StaticArrayBufferProto = new ArrayBuffer().__proto__; export function toString(thing) { if (typeof thing === 'string') { return thing; } + // eslint-disable-next-line new-cap return new ByteBuffer.wrap(thing).toString('binary'); } @@ -17,6 +17,7 @@ export function toArrayBuffer(thing) { return undefined; } if (thing === Object(thing)) { + // eslint-disable-next-line no-proto if (thing.__proto__ === StaticArrayBufferProto) { return thing; } @@ -25,6 +26,7 @@ export function toArrayBuffer(thing) { if (typeof thing !== 'string') { throw new Error(`Tried to convert a non-string of type ${typeof thing} to an array buffer`); } + // eslint-disable-next-line new-cap return new ByteBuffer.wrap(thing, 'binary').toArrayBuffer(); } diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js index 3f794e73ba9f..f64b243e0d88 100644 --- a/apps/meteor/app/e2e/client/rocketchat.e2e.room.js +++ b/apps/meteor/app/e2e/client/rocketchat.e2e.room.js @@ -1,9 +1,15 @@ import { Base64 } from '@rocket.chat/base64'; -import EJSON from 'ejson'; -import { Random } from '@rocket.chat/random'; import { Emitter } from '@rocket.chat/emitter'; +import { Random } from '@rocket.chat/random'; +import EJSON from 'ejson'; -import { e2e } from './rocketchat.e2e'; +import { RoomManager } from '../../../client/lib/RoomManager'; +import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; +import { RoomSettingsEnum } from '../../../definition/IRoomTypeConfig'; +import { ChatRoom, Subscriptions, Messages } from '../../models/client'; +import { Notifications } from '../../notifications/client'; +import { sdk } from '../../utils/client/lib/SDKClient'; +import { E2ERoomState } from './E2ERoomState'; import { toString, toArrayBuffer, @@ -19,14 +25,8 @@ import { importRSAKey, readFileAsArrayBuffer, } from './helper'; -import { Notifications } from '../../notifications/client'; -import { ChatRoom, Subscriptions, Messages } from '../../models/client'; import { log, logError } from './logger'; -import { E2ERoomState } from './E2ERoomState'; -import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; -import { RoomSettingsEnum } from '../../../definition/IRoomTypeConfig'; -import { RoomManager } from '../../../client/lib/RoomManager'; -import { sdk } from '../../utils/client/lib/SDKClient'; +import { e2e } from './rocketchat.e2e'; const KEY_ID = Symbol('keyID'); const PAUSED = Symbol('PAUSED'); diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.ts b/apps/meteor/app/e2e/client/rocketchat.e2e.ts index bedbd4d0aed7..42e9a3a99553 100644 --- a/apps/meteor/app/e2e/client/rocketchat.e2e.ts +++ b/apps/meteor/app/e2e/client/rocketchat.e2e.ts @@ -1,17 +1,28 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import URL from 'url'; import QueryString from 'querystring'; +import URL from 'url'; -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import type { ReactiveVar as ReactiveVarType } from 'meteor/reactive-var'; -import EJSON from 'ejson'; -import { Emitter } from '@rocket.chat/emitter'; import type { IE2EEMessage, IMessage, IRoom, ISubscription } from '@rocket.chat/core-typings'; import { isE2EEMessage } from '@rocket.chat/core-typings'; +import { Emitter } from '@rocket.chat/emitter'; +import EJSON from 'ejson'; +import { Meteor } from 'meteor/meteor'; +import type { ReactiveVar as ReactiveVarType } from 'meteor/reactive-var'; +import { ReactiveVar } from 'meteor/reactive-var'; +import * as banners from '../../../client/lib/banners'; +import type { LegacyBannerPayload } from '../../../client/lib/banners'; +import { imperativeModal } from '../../../client/lib/imperativeModal'; +import { mapMessageFromApi } from '../../../client/lib/utils/mapMessageFromApi'; +import { waitUntilFind } from '../../../client/lib/utils/waitUntilFind'; +import EnterE2EPasswordModal from '../../../client/views/e2e/EnterE2EPasswordModal'; +import SaveE2EPasswordModal from '../../../client/views/e2e/SaveE2EPasswordModal'; +import { createQuoteAttachment } from '../../../lib/createQuoteAttachment'; import { getMessageUrlRegex } from '../../../lib/getMessageUrlRegex'; -import { E2ERoom } from './rocketchat.e2e.room'; +import { ChatRoom, Subscriptions, Messages } from '../../models/client'; +import { settings } from '../../settings/client'; +import { getUserAvatarURL } from '../../utils/client'; +import { sdk } from '../../utils/client/lib/SDKClient'; +import { t } from '../../utils/lib/i18n'; import { toString, toArrayBuffer, @@ -26,22 +37,9 @@ import { deriveKey, generateMnemonicPhrase, } from './helper'; -import * as banners from '../../../client/lib/banners'; -import type { LegacyBannerPayload } from '../../../client/lib/banners'; -import { settings } from '../../settings/client'; -import { ChatRoom, Subscriptions, Messages } from '../../models/client'; -import './events.js'; -import './tabbar'; import { log, logError } from './logger'; -import { waitUntilFind } from '../../../client/lib/utils/waitUntilFind'; -import { imperativeModal } from '../../../client/lib/imperativeModal'; -import SaveE2EPasswordModal from '../../../client/views/e2e/SaveE2EPasswordModal'; -import EnterE2EPasswordModal from '../../../client/views/e2e/EnterE2EPasswordModal'; -import { getUserAvatarURL } from '../../utils/client'; -import { createQuoteAttachment } from '../../../lib/createQuoteAttachment'; -import { mapMessageFromApi } from '../../../client/lib/utils/mapMessageFromApi'; -import { t } from '../../utils/lib/i18n'; -import { sdk } from '../../utils/client/lib/SDKClient'; +import { E2ERoom } from './rocketchat.e2e.room'; +import './events.js'; let failedToDecodeKey = false; @@ -318,12 +316,12 @@ class E2E extends Emitter { return randomPassword; } - async encodePrivateKey(private_key: string, password: string): Promise { + async encodePrivateKey(privateKey: string, password: string): Promise { const masterKey = await this.getMasterKey(password); const vector = crypto.getRandomValues(new Uint8Array(16)); try { - const encodedPrivateKey = await encryptAES(vector, masterKey, toArrayBuffer(private_key)); + const encodedPrivateKey = await encryptAES(vector, masterKey, toArrayBuffer(privateKey)); return EJSON.stringify(joinVectorAndEcryptedData(vector, encodedPrivateKey)); } catch (error) { @@ -394,12 +392,12 @@ class E2E extends Emitter { }); } - async decodePrivateKey(private_key: string): Promise { + async decodePrivateKey(privateKey: string): Promise { const password = await this.requestPassword(); const masterKey = await this.getMasterKey(password); - const [vector, cipherText] = splitVectorAndEcryptedData(EJSON.parse(private_key)); + const [vector, cipherText] = splitVectorAndEcryptedData(EJSON.parse(privateKey)); try { const privKey = await decryptAES(vector, masterKey, cipherText); @@ -468,7 +466,7 @@ class E2E extends Emitter { await Promise.all( urls.map(async (url) => { - if (!url.includes(Meteor.absoluteUrl())) { + if (!url.includes(settings.get('Site_Url'))) { return; } diff --git a/apps/meteor/app/e2e/client/tabbar.ts b/apps/meteor/app/e2e/client/tabbar.ts deleted file mode 100644 index dac750540d40..000000000000 --- a/apps/meteor/app/e2e/client/tabbar.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useMemo, useCallback } from 'react'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetting, usePermission, useEndpoint } from '@rocket.chat/ui-contexts'; -import { isRoomFederated } from '@rocket.chat/core-typings'; - -import { addAction } from '../../../client/views/room/lib/Toolbox'; -import { useReactiveValue } from '../../../client/hooks/useReactiveValue'; -import { e2e } from './rocketchat.e2e'; - -addAction('e2e', ({ room }) => { - const e2eEnabled = useSetting('E2E_Enable'); - const e2eReady = useReactiveValue(useCallback(() => e2e.isReady(), [])) || room.encrypted; - const canToggleE2e = usePermission('toggle-room-e2e-encryption', room._id); - const canEditRoom = usePermission('edit-room', room._id); - const hasPermission = (room.t === 'd' || (canEditRoom && canToggleE2e)) && e2eReady; - const federated = isRoomFederated(room); - - const toggleE2E = useEndpoint('POST', '/v1/rooms.saveRoomSettings'); - - const action = useMutableCallback(() => { - void toggleE2E({ rid: room._id, encrypted: !room.encrypted }); - }); - - const enabledOnRoom = !!room.encrypted; - - return useMemo( - () => - e2eEnabled && hasPermission - ? { - groups: ['direct', 'direct_multiple', 'group', 'team'], - id: 'e2e', - title: enabledOnRoom ? 'E2E_disable' : 'E2E_enable', - icon: 'key', - order: 13, - action, - ...(federated && { - 'data-tooltip': 'E2E_unavailable_for_federation', - 'disabled': true, - }), - } - : null, - [action, e2eEnabled, enabledOnRoom, hasPermission, federated], - ); -}); diff --git a/apps/meteor/app/e2e/server/beforeCreateRoom.ts b/apps/meteor/app/e2e/server/beforeCreateRoom.ts index 6c71cb84b1bc..a9577204b3cb 100644 --- a/apps/meteor/app/e2e/server/beforeCreateRoom.ts +++ b/apps/meteor/app/e2e/server/beforeCreateRoom.ts @@ -1,7 +1,7 @@ -import { callbacks } from '../../../lib/callbacks'; +import { beforeCreateRoomCallback } from '../../../lib/callbacks/beforeCreateRoomCallback'; import { settings } from '../../settings/server'; -callbacks.add('beforeCreateRoom', ({ type, extraData }) => { +beforeCreateRoomCallback.add(({ type, extraData }) => { if ( settings.get('E2E_Enable') && ((type === 'd' && settings.get('E2E_Enabled_Default_DirectRooms')) || diff --git a/apps/meteor/app/e2e/server/functions/handleSuggestedGroupKey.ts b/apps/meteor/app/e2e/server/functions/handleSuggestedGroupKey.ts index 8b6d313e57e2..dcd1f82edbc8 100644 --- a/apps/meteor/app/e2e/server/functions/handleSuggestedGroupKey.ts +++ b/apps/meteor/app/e2e/server/functions/handleSuggestedGroupKey.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Subscriptions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; export async function handleSuggestedGroupKey( handle: 'accept' | 'reject', diff --git a/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts b/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts index c7fa5e075286..519317cb40fe 100644 --- a/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts +++ b/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts @@ -1,5 +1,5 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts b/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts index aaf3a0d9a2f9..cc586676482f 100644 --- a/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts +++ b/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IRoom, IUser } from '@rocket.chat/core-typings'; import { Subscriptions, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; diff --git a/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts b/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts index 77fb77858923..8c5add77a0bf 100644 --- a/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts +++ b/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import { api } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Subscriptions, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts b/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts index 4c5432e87f44..365feb1ac95d 100644 --- a/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts +++ b/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKey'; +import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts b/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts index 2fb296dab54d..005df0bb2a7a 100644 --- a/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts +++ b/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { Rooms } from '@rocket.chat/models'; import type { IRoom } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; @@ -31,7 +31,7 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' }); } - const room = await Rooms.findOneById(rid, { fields: { e2eKeyId: 1 } }); + const room = await Rooms.findOneById>(rid, { projection: { e2eKeyId: 1 } }); if (!room) { throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' }); diff --git a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts index b73addb54f62..c00b9b872466 100644 --- a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts +++ b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts @@ -1,5 +1,5 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/e2e/server/methods/updateGroupKey.ts b/apps/meteor/app/e2e/server/methods/updateGroupKey.ts index 84b1553a4422..30053cc7164a 100644 --- a/apps/meteor/app/e2e/server/methods/updateGroupKey.ts +++ b/apps/meteor/app/e2e/server/methods/updateGroupKey.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Subscriptions } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js index e895c9b41ae9..1340ead95789 100644 --- a/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js +++ b/apps/meteor/app/emoji-custom/client/lib/emojiCustom.js @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import $ from 'jquery'; +import { Meteor } from 'meteor/meteor'; +import { Session } from 'meteor/session'; -import { isSetNotNull } from './function-isSet'; -import { LegacyRoomManager } from '../../../ui-utils/client'; import { emoji, updateRecent } from '../../../emoji/client'; import { CachedCollectionManager } from '../../../ui-cached-collection/client'; +import { LegacyRoomManager } from '../../../ui-utils/client'; import { getURL } from '../../../utils/client'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { isSetNotNull } from './function-isSet'; export const getEmojiUrlFromName = function (name, extension) { if (name == null) { diff --git a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts index fe1d20fd0284..555d5544ab33 100644 --- a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { EmojiCustom } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ICustomEmojiDescriptor } from '@rocket.chat/core-typings'; +import { EmojiCustom } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; diff --git a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts index d432104239b8..874180097cb9 100644 --- a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts +++ b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import limax from 'limax'; -import { EmojiCustom } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; +import { EmojiCustom } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import limax from 'limax'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; +import { trim } from '../../../../lib/utils/stringUtils'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; -import { trim } from '../../../../lib/utils/stringUtils'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts index 6508ad760e36..485737b95f9b 100644 --- a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; +import type { IEmojiCustom } from '@rocket.chat/core-typings'; import { EmojiCustom } from '@rocket.chat/models'; -import { check, Match } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IEmojiCustom } from '@rocket.chat/core-typings'; +import { check, Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts index 429064c05660..ded98b7f5469 100644 --- a/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import limax from 'limax'; -import sharp from 'sharp'; import { api, Media } from '@rocket.chat/core-services'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import limax from 'limax'; +import { Meteor } from 'meteor/meteor'; +import sharp from 'sharp'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFile } from '../../../file/server'; diff --git a/apps/meteor/app/emoji-custom/server/startup/emoji-custom.js b/apps/meteor/app/emoji-custom/server/startup/emoji-custom.js index 0bce729e6e64..28e82cf0849e 100644 --- a/apps/meteor/app/emoji-custom/server/startup/emoji-custom.js +++ b/apps/meteor/app/emoji-custom/server/startup/emoji-custom.js @@ -2,13 +2,13 @@ import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; import _ from 'underscore'; -import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { RocketChatFile } from '../../../file/server'; +import { settings } from '../../../settings/server'; export let RocketChatFileEmojiCustomInstance; -Meteor.startup(function () { +Meteor.startup(() => { let storeType = 'GridFS'; if (settings.get('EmojiUpload_Storage_Type')) { @@ -35,7 +35,7 @@ Meteor.startup(function () { absolutePath: path, }); - return WebApp.connectHandlers.use('/emoji-custom/', async function (req, res /* , next*/) { + return WebApp.connectHandlers.use('/emoji-custom/', async (req, res /* , next*/) => { const params = { emoji: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, '')) }; if (_.isEmpty(params.emoji)) { diff --git a/apps/meteor/app/emoji-emojione/client/emojione-sprites.css b/apps/meteor/app/emoji-emojione/client/emojione-sprites.css index f6c4cf58512e..eb6d29ef768c 100644 --- a/apps/meteor/app/emoji-emojione/client/emojione-sprites.css +++ b/apps/meteor/app/emoji-emojione/client/emojione-sprites.css @@ -15,8 +15,8 @@ display: inline-block; overflow: hidden; - width: 22px; - height: 22px; + width: 1.375rem; + height: 1.375rem; margin: 0 0.15em; vertical-align: middle; diff --git a/apps/meteor/app/emoji-emojione/client/lib.ts b/apps/meteor/app/emoji-emojione/client/lib.ts index 408b053c20f8..53d8a2e1ea8b 100644 --- a/apps/meteor/app/emoji-emojione/client/lib.ts +++ b/apps/meteor/app/emoji-emojione/client/lib.ts @@ -2,8 +2,8 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/client'; import { getUserPreference } from '../../utils/client'; -import { isSetNotNull } from '../lib/isSetNotNull'; import { getEmojiConfig } from '../lib/getEmojiConfig'; +import { isSetNotNull } from '../lib/isSetNotNull'; const config = getEmojiConfig(); @@ -30,8 +30,8 @@ if (emoji.packages.emojione) { } // Additional settings -- ascii emojis - Meteor.startup(function () { - Tracker.autorun(async function () { + Meteor.startup(() => { + Tracker.autorun(async () => { if ((await isSetNotNull(() => emoji.packages.emojione)) && emoji.packages.emojione) { if (await isSetNotNull(() => getUserPreference(Meteor.userId() as string, 'convertAsciiEmoji'))) { emoji.packages.emojione.ascii = await getUserPreference(Meteor.userId() as string, 'convertAsciiEmoji'); diff --git a/apps/meteor/app/emoji-emojione/lib/generateEmojiIndex.mjs b/apps/meteor/app/emoji-emojione/lib/generateEmojiIndex.mjs index 1361a72c0dd2..009ef3dcc8f4 100644 --- a/apps/meteor/app/emoji-emojione/lib/generateEmojiIndex.mjs +++ b/apps/meteor/app/emoji-emojione/lib/generateEmojiIndex.mjs @@ -161,8 +161,8 @@ function generateEmojiPicker(data) { display: inline-block; overflow: hidden; - width: 22px; - height: 22px; + width: 1.375rem; + height: 1.375rem; margin: 0 0.15em; vertical-align: middle; diff --git a/apps/meteor/app/emoji-emojione/server/callbacks.ts b/apps/meteor/app/emoji-emojione/server/callbacks.ts index 4814cd7bcff1..5f73b73d9d4b 100644 --- a/apps/meteor/app/emoji-emojione/server/callbacks.ts +++ b/apps/meteor/app/emoji-emojione/server/callbacks.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import emojione from 'emojione'; +import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../lib/callbacks'; -Meteor.startup(function () { +Meteor.startup(() => { callbacks.add( 'beforeSendMessageNotifications', (message) => emojione.shortnameToUnicode(message), diff --git a/apps/meteor/app/emoji-emojione/server/lib.ts b/apps/meteor/app/emoji-emojione/server/lib.ts index b80b9318a828..57d366529526 100644 --- a/apps/meteor/app/emoji-emojione/server/lib.ts +++ b/apps/meteor/app/emoji-emojione/server/lib.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/server'; +import { getUserPreference } from '../../utils/server/lib/getUserPreference'; import { getEmojiConfig } from '../lib/getEmojiConfig'; import { isSetNotNull } from '../lib/isSetNotNull'; -import { getUserPreference } from '../../utils/server'; const config = getEmojiConfig(); @@ -33,7 +33,7 @@ if (emoji.packages.emojione) { } // Additional settings -- ascii emojis - Meteor.startup(async function () { + Meteor.startup(async () => { if ((await isSetNotNull(() => emoji.packages.emojione)) && emoji.packages.emojione) { if (await isSetNotNull(() => getUserPreference(Meteor.userId() as string, 'convertAsciiEmoji'))) { emoji.packages.emojione.ascii = await getUserPreference(Meteor.userId() as string, 'convertAsciiEmoji'); diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts index e4a549a1f02d..99a7497f13c7 100644 --- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts +++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Settings, Users, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; -import { sendMessage } from '../../../lib/server'; import { throttledCounter } from '../../../../lib/utils/throttledCounter'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; +import { settings } from '../../../settings/server'; const incException = throttledCounter((counter) => { Settings.incrementValueById('Uncaught_Exceptions_Count', counter).catch(console.error); diff --git a/apps/meteor/app/federation/server/endpoints/dispatch.js b/apps/meteor/app/federation/server/endpoints/dispatch.js index 1bd2fc67c096..59ffcb0f342f 100644 --- a/apps/meteor/app/federation/server/endpoints/dispatch.js +++ b/apps/meteor/app/federation/server/endpoints/dispatch.js @@ -1,21 +1,21 @@ -import EJSON from 'ejson'; -import { FederationServers, FederationRoomEvents, Rooms, Messages, Subscriptions, Users } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; import { eventTypes } from '@rocket.chat/core-typings'; +import { FederationServers, FederationRoomEvents, Rooms, Messages, Subscriptions, Users, ReadReceipts } from '@rocket.chat/models'; +import EJSON from 'ejson'; import { API } from '../../../api/server'; -import { serverLogger } from '../lib/logger'; -import { contextDefinitions } from '../lib/context'; -import { normalizers } from '../normalizers'; -import { deleteRoom } from '../../../lib/server/functions'; import { FileUpload } from '../../../file-upload/server'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { decryptIfNeeded } from '../lib/crypt'; -import { isFederationEnabled } from '../lib/isFederationEnabled'; -import { getUpload, requestEventsFromLatest } from '../handler'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; import { notifyUsersOnMessage } from '../../../lib/server/lib/notifyUsersOnMessage'; import { sendAllNotifications } from '../../../lib/server/lib/sendNotificationsOnMessage'; import { processThreads } from '../../../threads/server/hooks/aftersavemessage'; +import { getUpload, requestEventsFromLatest } from '../handler'; +import { contextDefinitions } from '../lib/context'; +import { decryptIfNeeded } from '../lib/crypt'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { isFederationEnabled } from '../lib/isFederationEnabled'; +import { serverLogger } from '../lib/logger'; +import { normalizers } from '../normalizers'; const eventHandlers = { // @@ -325,6 +325,7 @@ const eventHandlers = { // Remove the message await Messages.removeById(messageId); + await ReadReceipts.removeByMessageId(messageId); // Notify the room void api.broadcast('notify.deleteMessage', roomId, { _id: messageId }); @@ -456,8 +457,8 @@ const eventHandlers = { // Denormalize user const denormalizedUser = normalizers.denormalizeUser(user); - // Mute user - await Rooms.unmuteUsernameByRoomId(roomId, denormalizedUser.username); + // Unmute user + await Rooms.unmuteMutedUsernameByRoomId(roomId, denormalizedUser.username); } return eventResult; diff --git a/apps/meteor/app/federation/server/endpoints/requestFromLatest.js b/apps/meteor/app/federation/server/endpoints/requestFromLatest.js index 5938e3b3bea7..b45e8944e466 100644 --- a/apps/meteor/app/federation/server/endpoints/requestFromLatest.js +++ b/apps/meteor/app/federation/server/endpoints/requestFromLatest.js @@ -1,11 +1,11 @@ -import EJSON from 'ejson'; import { FederationRoomEvents } from '@rocket.chat/models'; +import EJSON from 'ejson'; import { API } from '../../../api/server'; -import { serverLogger } from '../lib/logger'; +import { dispatchEvents } from '../handler'; import { decryptIfNeeded } from '../lib/crypt'; import { isFederationEnabled } from '../lib/isFederationEnabled'; -import { dispatchEvents } from '../handler'; +import { serverLogger } from '../lib/logger'; API.v1.addRoute( 'federation.events.requestFromLatest', diff --git a/apps/meteor/app/federation/server/endpoints/users.js b/apps/meteor/app/federation/server/endpoints/users.js index ba0d7bda7d61..0d9a4ff4b0ce 100644 --- a/apps/meteor/app/federation/server/endpoints/users.js +++ b/apps/meteor/app/federation/server/endpoints/users.js @@ -1,9 +1,9 @@ import { Users } from '@rocket.chat/models'; import { API } from '../../../api/server'; -import { normalizers } from '../normalizers'; -import { serverLogger } from '../lib/logger'; import { isFederationEnabled } from '../lib/isFederationEnabled'; +import { serverLogger } from '../lib/logger'; +import { normalizers } from '../normalizers'; const userFields = { _id: 1, username: 1, type: 1, emails: 1, name: 1 }; diff --git a/apps/meteor/app/federation/server/functions/addUser.js b/apps/meteor/app/federation/server/functions/addUser.js index f3b422172ef0..8420a47944af 100644 --- a/apps/meteor/app/federation/server/functions/addUser.js +++ b/apps/meteor/app/federation/server/functions/addUser.js @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import { FederationServers, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import * as federationErrors from './errors'; import { getUserByUsername } from '../handler'; +import * as federationErrors from './errors'; export async function addUser(query) { if (!Meteor.userId()) { diff --git a/apps/meteor/app/federation/server/functions/dashboard.js b/apps/meteor/app/federation/server/functions/dashboard.js index 9d4bfefadc71..2fd54984b077 100644 --- a/apps/meteor/app/federation/server/functions/dashboard.js +++ b/apps/meteor/app/federation/server/functions/dashboard.js @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { FederationServers, FederationRoomEvents, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; export async function getStatistics() { const numberOfEvents = await FederationRoomEvents.col.estimatedDocumentCount(); diff --git a/apps/meteor/app/federation/server/functions/resolveDNS.ts b/apps/meteor/app/federation/server/functions/resolveDNS.ts index 52dd7653b114..3ba9bc8350eb 100644 --- a/apps/meteor/app/federation/server/functions/resolveDNS.ts +++ b/apps/meteor/app/federation/server/functions/resolveDNS.ts @@ -1,5 +1,5 @@ -import util from 'util'; import dns from 'dns'; +import util from 'util'; const dnsResolveSRV = util.promisify(dns.resolveSrv); const dnsResolveTXT = util.promisify(dns.resolveTxt); diff --git a/apps/meteor/app/federation/server/handler/index.js b/apps/meteor/app/federation/server/handler/index.js index 5f3f67e3ca16..c5b19856f19f 100644 --- a/apps/meteor/app/federation/server/handler/index.js +++ b/apps/meteor/app/federation/server/handler/index.js @@ -1,9 +1,9 @@ import qs from 'querystring'; import { disabled } from '../functions/errors'; -import { clientLogger } from '../lib/logger'; -import { isFederationEnabled } from '../lib/isFederationEnabled'; import { federationRequestToPeer } from '../lib/http'; +import { isFederationEnabled } from '../lib/isFederationEnabled'; +import { clientLogger } from '../lib/logger'; export async function federationSearchUsers(query) { if (!isFederationEnabled()) { diff --git a/apps/meteor/app/federation/server/hooks/afterAddedToRoom.js b/apps/meteor/app/federation/server/hooks/afterAddedToRoom.js index e9563b3469f4..ebf793b2d779 100644 --- a/apps/meteor/app/federation/server/hooks/afterAddedToRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterAddedToRoom.js @@ -1,11 +1,11 @@ import { FederationRoomEvents, Subscriptions } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; import { getFederatedRoomData, hasExternalDomain, isLocalUser, checkRoomType, checkRoomDomainsLength } from '../functions/helpers'; +import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; import { doAfterCreateRoom } from './afterCreateRoom'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { dispatchEvent } from '../handler'; async function afterAddedToRoom(involvedUsers, room) { const { user: addedUser } = involvedUsers; diff --git a/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js b/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js index cdad00cc439b..eeca4215095c 100644 --- a/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js @@ -1,11 +1,11 @@ import { FederationRoomEvents, Subscriptions } from '@rocket.chat/models'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; +import { isFullyQualified } from '../functions/helpers'; +import { dispatchEvents } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; -import { deleteRoom } from '../../../lib/server/functions'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { dispatchEvents } from '../handler'; -import { isFullyQualified } from '../functions/helpers'; async function afterCreateDirectRoom(room, extras) { clientLogger.debug({ msg: 'afterCreateDirectRoom', room, extras }); diff --git a/apps/meteor/app/federation/server/hooks/afterCreateRoom.js b/apps/meteor/app/federation/server/hooks/afterCreateRoom.js index 4caa3f711de3..2b4679b49212 100644 --- a/apps/meteor/app/federation/server/hooks/afterCreateRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterCreateRoom.js @@ -1,11 +1,11 @@ import { FederationRoomEvents, Users, Subscriptions } from '@rocket.chat/models'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; +import { checkRoomType, checkRoomDomainsLength } from '../functions/helpers'; +import { dispatchEvents } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; -import { deleteRoom } from '../../../lib/server/functions'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { dispatchEvents } from '../handler'; -import { checkRoomType, checkRoomDomainsLength } from '../functions/helpers'; export async function doAfterCreateRoom(room, users, subscriptions) { const normalizedUsers = []; diff --git a/apps/meteor/app/federation/server/hooks/afterDeleteMessage.js b/apps/meteor/app/federation/server/hooks/afterDeleteMessage.js index f7d7971c8186..4d2928c2ae31 100644 --- a/apps/meteor/app/federation/server/hooks/afterDeleteMessage.js +++ b/apps/meteor/app/federation/server/hooks/afterDeleteMessage.js @@ -1,9 +1,9 @@ import { FederationRoomEvents, Rooms } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; async function afterDeleteMessage(message) { const room = await Rooms.findOneById(message.rid, { projection: { federation: 1 } }); diff --git a/apps/meteor/app/federation/server/hooks/afterLeaveRoom.js b/apps/meteor/app/federation/server/hooks/afterLeaveRoom.js index e55e5180866f..a68db50ab850 100644 --- a/apps/meteor/app/federation/server/hooks/afterLeaveRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterLeaveRoom.js @@ -1,10 +1,10 @@ import { FederationRoomEvents } from '@rocket.chat/models'; import { getFederatedRoomData, hasExternalDomain, isLocalUser } from '../functions/helpers'; +import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { dispatchEvent } from '../handler'; async function afterLeaveRoom(user, room) { const localDomain = getFederationDomain(); diff --git a/apps/meteor/app/federation/server/hooks/afterMuteUser.js b/apps/meteor/app/federation/server/hooks/afterMuteUser.js index dc049ff303be..5221ccc64727 100644 --- a/apps/meteor/app/federation/server/hooks/afterMuteUser.js +++ b/apps/meteor/app/federation/server/hooks/afterMuteUser.js @@ -1,10 +1,10 @@ import { FederationRoomEvents } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; -import { normalizers } from '../normalizers'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; +import { normalizers } from '../normalizers'; async function afterMuteUser(involvedUsers, room) { // If there are not federated users on this room, ignore it diff --git a/apps/meteor/app/federation/server/hooks/afterRemoveFromRoom.js b/apps/meteor/app/federation/server/hooks/afterRemoveFromRoom.js index 382432802ae7..b2685dd8ac88 100644 --- a/apps/meteor/app/federation/server/hooks/afterRemoveFromRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterRemoveFromRoom.js @@ -1,10 +1,10 @@ import { FederationRoomEvents } from '@rocket.chat/models'; import { getFederatedRoomData, hasExternalDomain, isLocalUser } from '../functions/helpers'; +import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { dispatchEvent } from '../handler'; async function afterRemoveFromRoom(involvedUsers, room) { const { removedUser } = involvedUsers; diff --git a/apps/meteor/app/federation/server/hooks/afterSaveMessage.js b/apps/meteor/app/federation/server/hooks/afterSaveMessage.js index 7cf3a7d79645..7f67f4770686 100644 --- a/apps/meteor/app/federation/server/hooks/afterSaveMessage.js +++ b/apps/meteor/app/federation/server/hooks/afterSaveMessage.js @@ -1,10 +1,10 @@ import { FederationRoomEvents } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; -import { normalizers } from '../normalizers'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; +import { normalizers } from '../normalizers'; async function afterSaveMessage(message, room) { // If there are not federated users on this room, ignore it diff --git a/apps/meteor/app/federation/server/hooks/afterSetReaction.js b/apps/meteor/app/federation/server/hooks/afterSetReaction.js index d680b6a06e0f..485080dbd706 100644 --- a/apps/meteor/app/federation/server/hooks/afterSetReaction.js +++ b/apps/meteor/app/federation/server/hooks/afterSetReaction.js @@ -1,9 +1,9 @@ import { FederationRoomEvents, Rooms } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; async function afterSetReaction(message, { user, reaction }) { const room = await Rooms.findOneById(message.rid, { projection: { federation: 1 } }); diff --git a/apps/meteor/app/federation/server/hooks/afterUnmuteUser.js b/apps/meteor/app/federation/server/hooks/afterUnmuteUser.js index b3895a5454eb..57cfcee59601 100644 --- a/apps/meteor/app/federation/server/hooks/afterUnmuteUser.js +++ b/apps/meteor/app/federation/server/hooks/afterUnmuteUser.js @@ -1,10 +1,10 @@ import { FederationRoomEvents } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; -import { normalizers } from '../normalizers'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; +import { normalizers } from '../normalizers'; async function afterUnmuteUser(involvedUsers, room) { // If there are not federated users on this room, ignore it diff --git a/apps/meteor/app/federation/server/hooks/afterUnsetReaction.js b/apps/meteor/app/federation/server/hooks/afterUnsetReaction.js index f80572fadb66..51181d88ab9e 100644 --- a/apps/meteor/app/federation/server/hooks/afterUnsetReaction.js +++ b/apps/meteor/app/federation/server/hooks/afterUnsetReaction.js @@ -1,9 +1,9 @@ import { FederationRoomEvents, Rooms } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; async function afterUnsetReaction(message, { user, reaction }) { const room = Rooms.findOneById(message.rid, { fields: { federation: 1 } }); diff --git a/apps/meteor/app/federation/server/hooks/beforeDeleteRoom.js b/apps/meteor/app/federation/server/hooks/beforeDeleteRoom.js index a433b2a45fb4..c7105b4d177b 100644 --- a/apps/meteor/app/federation/server/hooks/beforeDeleteRoom.js +++ b/apps/meteor/app/federation/server/hooks/beforeDeleteRoom.js @@ -1,9 +1,9 @@ import { FederationRoomEvents, Rooms } from '@rocket.chat/models'; -import { clientLogger } from '../lib/logger'; import { hasExternalDomain } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; +import { clientLogger } from '../lib/logger'; async function beforeDeleteRoom(roomId) { const room = await Rooms.findOneById(roomId, { projection: { federation: 1 } }); diff --git a/apps/meteor/app/federation/server/lib/crypt.js b/apps/meteor/app/federation/server/lib/crypt.js index dde280524e20..786f15416ec5 100644 --- a/apps/meteor/app/federation/server/lib/crypt.js +++ b/apps/meteor/app/federation/server/lib/crypt.js @@ -1,7 +1,7 @@ import { FederationKeys } from '@rocket.chat/models'; -import { getFederationDomain } from './getFederationDomain'; import { search } from './dns'; +import { getFederationDomain } from './getFederationDomain'; import { cryptLogger } from './logger'; async function decrypt(data, peerKey) { diff --git a/apps/meteor/app/federation/server/lib/dns.js b/apps/meteor/app/federation/server/lib/dns.js index 928e4f9e4401..f8ef27e1dcf6 100644 --- a/apps/meteor/app/federation/server/lib/dns.js +++ b/apps/meteor/app/federation/server/lib/dns.js @@ -4,9 +4,9 @@ import util from 'util'; import mem from 'mem'; import * as federationErrors from '../functions/errors'; -import { dnsLogger } from './logger'; -import { isFederationEnabled } from './isFederationEnabled'; import { federationRequest } from './http'; +import { isFederationEnabled } from './isFederationEnabled'; +import { dnsLogger } from './logger'; const dnsResolveSRV = util.promisify(dnsResolver.resolveSrv); const dnsResolveTXT = util.promisify(dnsResolver.resolveTxt); diff --git a/apps/meteor/app/federation/server/lib/http.js b/apps/meteor/app/federation/server/lib/http.js index 4b167a4220cf..d663d93d2f15 100644 --- a/apps/meteor/app/federation/server/lib/http.js +++ b/apps/meteor/app/federation/server/lib/http.js @@ -1,10 +1,10 @@ -import EJSON from 'ejson'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import EJSON from 'ejson'; -import { httpLogger } from './logger'; -import { getFederationDomain } from './getFederationDomain'; -import { search } from './dns'; import { encrypt } from './crypt'; +import { search } from './dns'; +import { getFederationDomain } from './getFederationDomain'; +import { httpLogger } from './logger'; export async function federationRequest(method, url, body, headers, peerKey = null) { let data = null; diff --git a/apps/meteor/app/federation/server/lib/logger.js b/apps/meteor/app/federation/server/lib/logger.js index 9e66d33808b0..e8cf0ae12844 100644 --- a/apps/meteor/app/federation/server/lib/logger.js +++ b/apps/meteor/app/federation/server/lib/logger.js @@ -1,4 +1,4 @@ -import { Logger } from '../../../logger/server'; +import { Logger } from '@rocket.chat/logger'; const logger = new Logger('Federation'); diff --git a/apps/meteor/app/federation/server/methods/loadContextEvents.ts b/apps/meteor/app/federation/server/methods/loadContextEvents.ts index f8b93698384a..9e286bbf913a 100644 --- a/apps/meteor/app/federation/server/methods/loadContextEvents.ts +++ b/apps/meteor/app/federation/server/methods/loadContextEvents.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import type { IFederationEvent } from '@rocket.chat/core-typings'; import { FederationRoomEvents } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IFederationEvent } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/federation/server/methods/testSetup.ts b/apps/meteor/app/federation/server/methods/testSetup.ts index 5f29614ab651..93e501535474 100644 --- a/apps/meteor/app/federation/server/methods/testSetup.ts +++ b/apps/meteor/app/federation/server/methods/testSetup.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { eventTypes } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvent } from '../handler'; +import { getFederationDomain } from '../lib/getFederationDomain'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/federation/server/normalizers/user.js b/apps/meteor/app/federation/server/normalizers/user.js index fda3d6a1c601..a2a55e07aea2 100644 --- a/apps/meteor/app/federation/server/normalizers/user.js +++ b/apps/meteor/app/federation/server/normalizers/user.js @@ -1,5 +1,5 @@ -import _ from 'underscore'; import { Users } from '@rocket.chat/models'; +import _ from 'underscore'; import { getNameAndDomain, isFullyQualified } from '../functions/helpers'; import { getFederationDomain } from '../lib/getFederationDomain'; diff --git a/apps/meteor/app/federation/server/startup/registerCallbacks.js b/apps/meteor/app/federation/server/startup/registerCallbacks.js index 6702140d038c..54f5ee443450 100644 --- a/apps/meteor/app/federation/server/startup/registerCallbacks.js +++ b/apps/meteor/app/federation/server/startup/registerCallbacks.js @@ -1,4 +1,3 @@ -import { registerCallback } from '../lib/callbacks'; import { definition as afterAddedToRoomDef } from '../hooks/afterAddedToRoom'; import { definition as afterCreateDirectRoomDef } from '../hooks/afterCreateDirectRoom'; import { definition as afterCreateRoomDef } from '../hooks/afterCreateRoom'; @@ -11,6 +10,7 @@ import { definition as afterSetReactionDef } from '../hooks/afterSetReaction'; import { definition as afterUnmuteUserDef } from '../hooks/afterUnmuteUser'; import { definition as afterUnsetReactionDef } from '../hooks/afterUnsetReaction'; import { definition as beforeDeleteRoomDef } from '../hooks/beforeDeleteRoom'; +import { registerCallback } from '../lib/callbacks'; registerCallback(afterAddedToRoomDef); registerCallback(afterCreateDirectRoomDef); diff --git a/apps/meteor/app/federation/server/startup/settings.ts b/apps/meteor/app/federation/server/startup/settings.ts index cf3131d09aa8..4c3ae7650164 100644 --- a/apps/meteor/app/federation/server/startup/settings.ts +++ b/apps/meteor/app/federation/server/startup/settings.ts @@ -1,13 +1,13 @@ import { FederationKeys } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; +import { STATUS_ENABLED, STATUS_REGISTERING, STATUS_ERROR_REGISTERING, STATUS_DISABLED } from '../constants'; import { updateStatus, updateEnabled, isRegisteringOrEnabled } from '../functions/helpers'; -import { getFederationDomain } from '../lib/getFederationDomain'; -import { getFederationDiscoveryMethod } from '../lib/getFederationDiscoveryMethod'; -import { registerWithHub } from '../lib/dns'; import { enableCallbacks, disableCallbacks } from '../lib/callbacks'; +import { registerWithHub } from '../lib/dns'; +import { getFederationDiscoveryMethod } from '../lib/getFederationDiscoveryMethod'; +import { getFederationDomain } from '../lib/getFederationDomain'; import { setupLogger } from '../lib/logger'; -import { STATUS_ENABLED, STATUS_REGISTERING, STATUS_ERROR_REGISTERING, STATUS_DISABLED } from '../constants'; const updateSettings = async function (): Promise { // Get the key pair diff --git a/apps/meteor/app/file-upload/client/lib/fileUploadHandler.ts b/apps/meteor/app/file-upload/client/lib/fileUploadHandler.ts index 114b3e5d080c..e0887888c478 100644 --- a/apps/meteor/app/file-upload/client/lib/fileUploadHandler.ts +++ b/apps/meteor/app/file-upload/client/lib/fileUploadHandler.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -Tracker.autorun(function () { +Tracker.autorun(() => { const userId = Meteor.userId(); if (userId) { diff --git a/apps/meteor/app/file-upload/server/config/AmazonS3.ts b/apps/meteor/app/file-upload/server/config/AmazonS3.ts index 1c4d8a838800..b97ff60d86d6 100644 --- a/apps/meteor/app/file-upload/server/config/AmazonS3.ts +++ b/apps/meteor/app/file-upload/server/config/AmazonS3.ts @@ -5,8 +5,8 @@ import URL from 'url'; import _ from 'underscore'; import { settings } from '../../../settings/server'; -import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import type { S3Options } from '../../ufs/AmazonS3/server'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import '../../ufs/AmazonS3/server'; const get: FileUploadClass['get'] = async function (this: FileUploadClass, file, req, res) { @@ -61,7 +61,7 @@ const AmazonS3UserDataFiles = new FileUploadClass({ // store setted bellow }); -const configure = _.debounce(function () { +const configure = _.debounce(() => { const Bucket = settings.get('FileUpload_S3_Bucket'); const Acl = settings.get('FileUpload_S3_Acl'); const AWSAccessKeyId = settings.get('FileUpload_S3_AWSAccessKeyId'); diff --git a/apps/meteor/app/file-upload/server/config/FileSystem.ts b/apps/meteor/app/file-upload/server/config/FileSystem.ts index e4b6afb80a0d..98342daf2e46 100644 --- a/apps/meteor/app/file-upload/server/config/FileSystem.ts +++ b/apps/meteor/app/file-upload/server/config/FileSystem.ts @@ -127,7 +127,7 @@ const FileSystemUserDataFiles = new FileUploadClass({ }, }); -settings.watch('FileUpload_FileSystemPath', function () { +settings.watch('FileUpload_FileSystemPath', () => { const options = { path: settings.get('FileUpload_FileSystemPath'), // '/tmp/uploads/photos', }; diff --git a/apps/meteor/app/file-upload/server/config/GoogleStorage.ts b/apps/meteor/app/file-upload/server/config/GoogleStorage.ts index c5daedfedb0f..124bad4365a0 100644 --- a/apps/meteor/app/file-upload/server/config/GoogleStorage.ts +++ b/apps/meteor/app/file-upload/server/config/GoogleStorage.ts @@ -4,8 +4,8 @@ import URL from 'url'; import _ from 'underscore'; -import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import { settings } from '../../../settings/server'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import '../../ufs/GoogleStorage/server'; const get: FileUploadClass['get'] = async function (this: FileUploadClass, file, req, res) { @@ -61,7 +61,7 @@ const GoogleCloudStorageUserDataFiles = new FileUploadClass({ // store setted bellow }); -const configure = _.debounce(function () { +const configure = _.debounce(() => { const bucket = settings.get('FileUpload_GoogleStorage_Bucket'); const projectId = settings.get('FileUpload_GoogleStorage_ProjectId'); const accessId = settings.get('FileUpload_GoogleStorage_AccessId'); diff --git a/apps/meteor/app/file-upload/server/config/GridFS.ts b/apps/meteor/app/file-upload/server/config/GridFS.ts index 399267650ec2..629d177581bf 100644 --- a/apps/meteor/app/file-upload/server/config/GridFS.ts +++ b/apps/meteor/app/file-upload/server/config/GridFS.ts @@ -1,12 +1,12 @@ +import type * as http from 'http'; import type { TransformCallback, TransformOptions } from 'stream'; import stream from 'stream'; import zlib from 'zlib'; -import type * as http from 'http'; import type { IUpload } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import { UploadFS } from '../../../../server/ufs'; -import { Logger } from '../../../logger/server'; import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import { getFileRange, setRangeHeaders } from '../lib/ranges'; @@ -71,13 +71,13 @@ const readFromGridFS = async function ( const ws = new stream.PassThrough(); [rs, ws].forEach((stream) => - stream.on('error', function (err) { + stream.on('error', (err) => { store.onReadError.call(store, err, fileId, file); res.end(); }), ); - ws.on('close', function () { + ws.on('close', () => { // Close output stream at the end ws.emit('end'); }); @@ -131,7 +131,7 @@ const copyFromGridFS = async function (storeName: string | undefined, fileId: st const rs = await store.getReadStream(fileId, file); [rs, out].forEach((stream) => - stream.on('error', function (err) { + stream.on('error', (err) => { store.onReadError.call(store, err, fileId, file); out.end(); }), diff --git a/apps/meteor/app/file-upload/server/config/Webdav.ts b/apps/meteor/app/file-upload/server/config/Webdav.ts index 3a68e699656e..fb8c1ca82ca4 100644 --- a/apps/meteor/app/file-upload/server/config/Webdav.ts +++ b/apps/meteor/app/file-upload/server/config/Webdav.ts @@ -1,9 +1,9 @@ import _ from 'underscore'; -import { FileUploadClass, FileUpload } from '../lib/FileUpload'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; +import { FileUploadClass, FileUpload } from '../lib/FileUpload'; import '../../ufs/Webdav/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; const get: FileUploadClass['get'] = async function (this: FileUploadClass, file, _req, res) { (await this.store.getReadStream(file._id, file)) @@ -43,7 +43,7 @@ const WebdavUserDataFiles = new FileUploadClass({ // store setted bellow }); -const configure = _.debounce(function () { +const configure = _.debounce(() => { const uploadFolderPath = settings.get('FileUpload_Webdav_Upload_Folder_Path'); const server = settings.get('FileUpload_Webdav_Server_URL'); const username = settings.get('FileUpload_Webdav_Username'); diff --git a/apps/meteor/app/file-upload/server/config/_configUploadStorage.ts b/apps/meteor/app/file-upload/server/config/_configUploadStorage.ts index e2d736ae5104..55cc1afbbab1 100644 --- a/apps/meteor/app/file-upload/server/config/_configUploadStorage.ts +++ b/apps/meteor/app/file-upload/server/config/_configUploadStorage.ts @@ -1,8 +1,8 @@ import _ from 'underscore'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { UploadFS } from '../../../../server/ufs'; import { settings } from '../../../settings/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; import './AmazonS3'; import './FileSystem'; import './GoogleStorage'; diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.ts b/apps/meteor/app/file-upload/server/lib/FileUpload.ts index 9864bd4a4bd6..8f929a17fe34 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.ts +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.ts @@ -1,44 +1,44 @@ +import { Buffer } from 'buffer'; import type { WriteStream } from 'fs'; import fs from 'fs'; import { unlink, rename, writeFile } from 'fs/promises'; -import stream from 'stream'; import type * as http from 'http'; import type * as https from 'https'; -import { Buffer } from 'buffer'; +import stream from 'stream'; import URL from 'url'; -import { Meteor } from 'meteor/meteor'; -import type { WritableStreamBuffer } from 'stream-buffers'; -import streamBuffers from 'stream-buffers'; -import sharp from 'sharp'; -import { Cookies } from 'meteor/ostrio:cookies'; -import { Match } from 'meteor/check'; -import { Users, Avatars, UserDataFiles, Uploads, Settings, Subscriptions, Messages, Rooms } from '@rocket.chat/models'; -import filesize from 'filesize'; -import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { hashLoginToken } from '@rocket.chat/account-utils'; +import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import type { IUpload } from '@rocket.chat/core-typings'; +import { Users, Avatars, UserDataFiles, Uploads, Settings, Subscriptions, Messages, Rooms } from '@rocket.chat/models'; import type { NextFunction } from 'connect'; +import filesize from 'filesize'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { Cookies } from 'meteor/ostrio:cookies'; import type { OptionalId } from 'mongodb'; +import sharp from 'sharp'; +import type { WritableStreamBuffer } from 'stream-buffers'; +import streamBuffers from 'stream-buffers'; +import { AppEvents, Apps } from '../../../../ee/server/apps'; +import { i18n } from '../../../../server/lib/i18n'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { UploadFS } from '../../../../server/ufs'; +import { ufsComplete } from '../../../../server/ufs/ufs-methods'; +import type { Store, StoreOptions } from '../../../../server/ufs/ufs-store'; +import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; import { settings } from '../../../settings/server'; import { mime } from '../../../utils/lib/mimeTypes'; -import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; -import { fileUploadIsValidContentType } from '../../../utils/server/restrictions'; import { isValidJWT, generateJWT } from '../../../utils/server/lib/JWTHelper'; -import { AppEvents, Apps } from '../../../../ee/server/apps'; +import { fileUploadIsValidContentType } from '../../../utils/server/restrictions'; import { streamToBuffer } from './streamToBuffer'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import type { Store, StoreOptions } from '../../../../server/ufs/ufs-store'; -import { ufsComplete } from '../../../../server/ufs/ufs-methods'; -import { i18n } from '../../../../server/lib/i18n'; const cookie = new Cookies(); let maxFileSize = 0; -settings.watch('FileUpload_MaxFileSize', async function (value: string) { +settings.watch('FileUpload_MaxFileSize', async (value: string) => { try { maxFileSize = parseInt(value); } catch (e) { diff --git a/apps/meteor/app/file-upload/server/lib/proxy.ts b/apps/meteor/app/file-upload/server/lib/proxy.ts index 639ace6aa040..048424af4bd9 100644 --- a/apps/meteor/app/file-upload/server/lib/proxy.ts +++ b/apps/meteor/app/file-upload/server/lib/proxy.ts @@ -1,15 +1,15 @@ import http from 'http'; import URL from 'url'; -import { WebApp } from 'meteor/webapp'; import { InstanceStatus } from '@rocket.chat/instance-status'; +import { Logger } from '@rocket.chat/logger'; import { InstanceStatus as InstanceStatusModel } from '@rocket.chat/models'; import type { NextFunction } from 'connect'; import type createServer from 'connect'; +import { WebApp } from 'meteor/webapp'; import { UploadFS } from '../../../../server/ufs'; -import { Logger } from '../../../logger/server'; -import { isDocker } from '../../../utils/server'; +import { isDocker } from '../../../utils/server/functions/isDocker'; const logger = new Logger('UploadProxy'); @@ -88,7 +88,7 @@ async function handle(req: createServer.IncomingMessage, res: http.ServerRespons 'UFS proxy middleware is deprecated as this upload method is not being used by Web/Mobile Clients. See this: https://docs.rocket.chat/api/rest-api/methods/rooms/upload', ); // eslint-disable-next-line @typescript-eslint/naming-convention - const proxy = http.request(options, function (proxy_res) { + const proxy = http.request(options, (proxy_res) => { proxy_res.pipe(res, { end: true, }); diff --git a/apps/meteor/app/file-upload/server/lib/requests.ts b/apps/meteor/app/file-upload/server/lib/requests.ts index 91bb50b179d8..d50a7d594ca7 100644 --- a/apps/meteor/app/file-upload/server/lib/requests.ts +++ b/apps/meteor/app/file-upload/server/lib/requests.ts @@ -1,9 +1,9 @@ -import { WebApp } from 'meteor/webapp'; import { Uploads } from '@rocket.chat/models'; +import { WebApp } from 'meteor/webapp'; import { FileUpload } from './FileUpload'; -WebApp.connectHandlers.use(FileUpload.getPath(), async function (req, res, next) { +WebApp.connectHandlers.use(FileUpload.getPath(), async (req, res, next) => { const match = /^\/([^\/]+)\/(.*)/.exec(req.url || ''); if (match?.[1]) { diff --git a/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts b/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts index fbec0a0c35d2..fdb8d131916d 100644 --- a/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts +++ b/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { Rooms, Uploads } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { UploadFS } from '../../../../server/ufs'; -import { settings } from '../../../settings/server'; import { canAccessRoomAsync } from '../../../authorization/server'; +import { settings } from '../../../settings/server'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts index 2391c225b041..d922b5adab78 100644 --- a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts +++ b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts @@ -1,16 +1,16 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import type { MessageAttachment, FileAttachmentProps, IUser, IUpload, AtLeast } from '@rocket.chat/core-typings'; import { Rooms, Uploads, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { FileUpload } from '../lib/FileUpload'; -import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { omit } from '../../../../lib/utils/omit'; import { getFileExtension } from '../../../../lib/utils/getFileExtension'; +import { omit } from '../../../../lib/utils/omit'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; +import { FileUpload } from '../lib/FileUpload'; function validateFileRequiredFields(file: Partial): asserts file is AtLeast { const requiredFields = ['_id', 'name', 'type', 'size']; diff --git a/apps/meteor/app/file-upload/ufs/AmazonS3/server.ts b/apps/meteor/app/file-upload/ufs/AmazonS3/server.ts index 5ec84c563432..b9f0807b6112 100644 --- a/apps/meteor/app/file-upload/ufs/AmazonS3/server.ts +++ b/apps/meteor/app/file-upload/ufs/AmazonS3/server.ts @@ -1,14 +1,14 @@ import stream from 'stream'; -import { check } from 'meteor/check'; +import type { IUpload } from '@rocket.chat/core-typings'; import { Random } from '@rocket.chat/random'; -import _ from 'underscore'; import S3 from 'aws-sdk/clients/s3'; +import { check } from 'meteor/check'; import type { OptionalId } from 'mongodb'; -import type { IUpload } from '@rocket.chat/core-typings'; +import _ from 'underscore'; -import { UploadFS } from '../../../../server/ufs'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { UploadFS } from '../../../../server/ufs'; import type { StoreOptions } from '../../../../server/ufs/ufs-store'; export type S3Options = StoreOptions & { diff --git a/apps/meteor/app/file-upload/ufs/GoogleStorage/server.ts b/apps/meteor/app/file-upload/ufs/GoogleStorage/server.ts index 8b89bb0d5adb..57988df59a51 100644 --- a/apps/meteor/app/file-upload/ufs/GoogleStorage/server.ts +++ b/apps/meteor/app/file-upload/ufs/GoogleStorage/server.ts @@ -1,12 +1,12 @@ -import { check } from 'meteor/check'; -import { Random } from '@rocket.chat/random'; import type { GetSignedUrlConfig } from '@google-cloud/storage'; import { Storage } from '@google-cloud/storage'; -import type { OptionalId } from 'mongodb'; import type { IUpload } from '@rocket.chat/core-typings'; +import { Random } from '@rocket.chat/random'; +import { check } from 'meteor/check'; +import type { OptionalId } from 'mongodb'; -import { UploadFS } from '../../../../server/ufs'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { UploadFS } from '../../../../server/ufs'; import type { StoreOptions } from '../../../../server/ufs/ufs-store'; type GStoreOptions = StoreOptions & { diff --git a/apps/meteor/app/file-upload/ufs/Webdav/server.ts b/apps/meteor/app/file-upload/ufs/Webdav/server.ts index 79aca248d3a0..69ab18a4ebb0 100644 --- a/apps/meteor/app/file-upload/ufs/Webdav/server.ts +++ b/apps/meteor/app/file-upload/ufs/Webdav/server.ts @@ -1,14 +1,14 @@ import stream from 'stream'; -import { check } from 'meteor/check'; +import type { IUpload } from '@rocket.chat/core-typings'; import { Random } from '@rocket.chat/random'; +import { check } from 'meteor/check'; import type { OptionalId } from 'mongodb'; -import type { IUpload } from '@rocket.chat/core-typings'; -import { UploadFS } from '../../../../server/ufs'; -import { WebdavClientAdapter } from '../../../webdav/server/lib/webdavClientAdapter'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { UploadFS } from '../../../../server/ufs'; import type { StoreOptions } from '../../../../server/ufs/ufs-store'; +import { WebdavClientAdapter } from '../../../webdav/server/lib/webdavClientAdapter'; type WebdavOptions = StoreOptions & { connection: { @@ -137,7 +137,7 @@ class WebdavStore extends UploadFS.Store { process.nextTick(() => { writeStream.removeListener(event, listener); writeStream.removeListener('newListener', newListenerCallback); - writeStream.on(event, function () { + writeStream.on(event, () => { setTimeout(listener, 500); }); }); diff --git a/apps/meteor/app/file/server/file.server.ts b/apps/meteor/app/file/server/file.server.ts index ebd869e690b7..e0190d06a729 100644 --- a/apps/meteor/app/file/server/file.server.ts +++ b/apps/meteor/app/file/server/file.server.ts @@ -1,14 +1,14 @@ -import stream from 'stream'; import type { ReadStream } from 'fs'; import fs from 'fs'; import fsp from 'fs/promises'; import path from 'path'; +import stream from 'stream'; +import type { ObjectId } from 'bson'; import { MongoInternals } from 'meteor/mongo'; import mkdirp from 'mkdirp'; import type { GridFSBucketReadStream } from 'mongodb'; import { GridFSBucket } from 'mongodb'; -import type { ObjectId } from 'bson'; const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo; @@ -69,7 +69,7 @@ class GridFS implements IRocketChatFileStore { contentType, }); - ws.on('close', function () { + ws.on('close', () => { return ws.emit('end'); }); return ws; @@ -100,11 +100,11 @@ class GridFS implements IRocketChatFileStore { } return new Promise((resolve) => { const data: Buffer[] = []; - file.readStream.on('data', function (chunk) { + file.readStream.on('data', (chunk) => { return data.push(chunk); }); - file.readStream.on('end', function () { + file.readStream.on('end', () => { resolve({ buffer: Buffer.concat(data), contentType: file.contentType, @@ -142,7 +142,7 @@ class FileSystem implements IRocketChatFileStore { createWriteStream(fileName: string) { const ws = fs.createWriteStream(path.join(this.absolutePath, fileName)); - ws.on('close', function () { + ws.on('close', () => { return ws.emit('end'); }); return ws; @@ -181,10 +181,10 @@ class FileSystem implements IRocketChatFileStore { } return new Promise((resolve) => { const data: Buffer[] = []; - file.readStream.on('data', function (chunk: Buffer) { + file.readStream.on('data', (chunk: Buffer) => { return data.push(chunk); }); - file.readStream.on('end', function () { + file.readStream.on('end', () => { resolve({ buffer: Buffer.concat(data), length: file.length, @@ -195,7 +195,7 @@ class FileSystem implements IRocketChatFileStore { async deleteFile(fileName: string) { try { - return this.remove(fileName); + return await this.remove(fileName); } catch (error1) { // } diff --git a/apps/meteor/app/github-enterprise/client/lib.ts b/apps/meteor/app/github-enterprise/client/lib.ts index 9d9eae61400f..ec03985df0cf 100644 --- a/apps/meteor/app/github-enterprise/client/lib.ts +++ b/apps/meteor/app/github-enterprise/client/lib.ts @@ -1,6 +1,6 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import type { OauthConfig } from '@rocket.chat/core-typings'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; import { settings } from '../../settings/client'; @@ -20,8 +20,8 @@ const config: OauthConfig = { }; const GitHubEnterprise = new CustomOAuth('github_enterprise', config); -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { if (settings.get('API_GitHub_Enterprise_URL')) { config.serverURL = settings.get('API_GitHub_Enterprise_URL'); GitHubEnterprise.configure(config); diff --git a/apps/meteor/app/github-enterprise/server/lib.ts b/apps/meteor/app/github-enterprise/server/lib.ts index f8adfb841b8d..b4f67a1c3a50 100644 --- a/apps/meteor/app/github-enterprise/server/lib.ts +++ b/apps/meteor/app/github-enterprise/server/lib.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import type { OauthConfig } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { settings } from '../../settings/server'; @@ -20,8 +20,8 @@ const config: OauthConfig = { const GitHubEnterprise = new CustomOAuth('github_enterprise', config); -Meteor.startup(function () { - settings.watch('API_GitHub_Enterprise_URL', function (value) { +Meteor.startup(() => { + settings.watch('API_GitHub_Enterprise_URL', (value) => { config.serverURL = value; GitHubEnterprise.configure(config); }); diff --git a/apps/meteor/app/gitlab/client/lib.ts b/apps/meteor/app/gitlab/client/lib.ts index 7c0644b6721e..a1b2ded0cc1a 100644 --- a/apps/meteor/app/gitlab/client/lib.ts +++ b/apps/meteor/app/gitlab/client/lib.ts @@ -1,9 +1,9 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/client'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; +import { settings } from '../../settings/client'; const config: OauthConfig = { serverURL: 'https://gitlab.com', @@ -19,8 +19,8 @@ const config: OauthConfig = { const Gitlab = new CustomOAuth('gitlab', config); -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { let anyChange = false; if (settings.get('API_Gitlab_URL')) { config.serverURL = settings.get('API_Gitlab_URL').trim().replace(/\/*$/, ''); diff --git a/apps/meteor/app/gitlab/server/lib.ts b/apps/meteor/app/gitlab/server/lib.ts index 0800b654b657..0f4d330cf597 100644 --- a/apps/meteor/app/gitlab/server/lib.ts +++ b/apps/meteor/app/gitlab/server/lib.ts @@ -1,9 +1,9 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/server'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { settings } from '../../settings/server'; const config: OauthConfig = { serverURL: 'https://gitlab.com', @@ -19,7 +19,7 @@ const config: OauthConfig = { const Gitlab = new CustomOAuth('gitlab', config); -Meteor.startup(function () { +Meteor.startup(() => { const updateConfig = _.debounce(() => { config.serverURL = settings.get('API_Gitlab_URL').trim().replace(/\/*$/, '') || config.serverURL; config.identityPath = settings.get('Accounts_OAuth_Gitlab_identity_path') || config.identityPath; diff --git a/apps/meteor/app/iframe-login/server/iframe_server.ts b/apps/meteor/app/iframe-login/server/iframe_server.ts index 26fd87e38f8c..c7e67edf9deb 100644 --- a/apps/meteor/app/iframe-login/server/iframe_server.ts +++ b/apps/meteor/app/iframe-login/server/iframe_server.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; +import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Accounts } from 'meteor/accounts-base'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { OAuth } from 'meteor/oauth'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { Users } from '@rocket.chat/models'; -Accounts.registerLoginHandler('iframe', async function (result) { +Accounts.registerLoginHandler('iframe', async (result) => { if (!result.iframe) { return; } diff --git a/apps/meteor/app/importer-csv/server/index.ts b/apps/meteor/app/importer-csv/server/index.ts index da1eaacdd035..8ab31878fb7b 100644 --- a/apps/meteor/app/importer-csv/server/index.ts +++ b/apps/meteor/app/importer-csv/server/index.ts @@ -1,5 +1,5 @@ -import { CsvImporter } from './importer'; import { Importers } from '../../importer/server'; import { CsvImporterInfo } from '../lib/info'; +import { CsvImporter } from './importer'; Importers.add(new CsvImporterInfo(), CsvImporter); diff --git a/apps/meteor/app/importer-hipchat-enterprise/server/importer.js b/apps/meteor/app/importer-hipchat-enterprise/server/importer.js index 96a8e7a8a660..18fefea074bf 100644 --- a/apps/meteor/app/importer-hipchat-enterprise/server/importer.js +++ b/apps/meteor/app/importer-hipchat-enterprise/server/importer.js @@ -1,9 +1,9 @@ -import { Readable } from 'stream'; -import path from 'path'; import fs from 'fs'; +import path from 'path'; +import { Readable } from 'stream'; -import { Meteor } from 'meteor/meteor'; import { Settings } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { Base, ProgressStep } from '../../importer/server'; diff --git a/apps/meteor/app/importer-hipchat-enterprise/server/index.ts b/apps/meteor/app/importer-hipchat-enterprise/server/index.ts index 11f9a8e7b4b6..097a5071306d 100644 --- a/apps/meteor/app/importer-hipchat-enterprise/server/index.ts +++ b/apps/meteor/app/importer-hipchat-enterprise/server/index.ts @@ -1,5 +1,5 @@ -import { HipChatEnterpriseImporter } from './importer'; import { Importers } from '../../importer/server'; import { HipChatEnterpriseImporterInfo } from '../lib/info'; +import { HipChatEnterpriseImporter } from './importer'; Importers.add(new HipChatEnterpriseImporterInfo(), HipChatEnterpriseImporter); diff --git a/apps/meteor/app/importer-pending-avatars/server/index.ts b/apps/meteor/app/importer-pending-avatars/server/index.ts index 904fa05e6bce..9c6bc278abee 100644 --- a/apps/meteor/app/importer-pending-avatars/server/index.ts +++ b/apps/meteor/app/importer-pending-avatars/server/index.ts @@ -1,5 +1,5 @@ -import { PendingAvatarImporter } from './importer'; import { Importers } from '../../importer/server'; +import { PendingAvatarImporter } from './importer'; import { PendingAvatarImporterInfo } from './info'; Importers.add(new PendingAvatarImporterInfo(), PendingAvatarImporter); diff --git a/apps/meteor/app/importer-pending-files/server/importer.js b/apps/meteor/app/importer-pending-files/server/importer.js index 69bc24a90711..3d8d083c495b 100644 --- a/apps/meteor/app/importer-pending-files/server/importer.js +++ b/apps/meteor/app/importer-pending-files/server/importer.js @@ -1,11 +1,11 @@ -import https from 'https'; import http from 'http'; +import https from 'https'; -import { Random } from '@rocket.chat/random'; import { Messages } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; -import { Base, ProgressStep, Selection } from '../../importer/server'; import { FileUpload } from '../../file-upload/server'; +import { Base, ProgressStep, Selection } from '../../importer/server'; export class PendingFileImporter extends Base { constructor(info, importRecord) { @@ -113,7 +113,7 @@ export class PendingFileImporter extends Base { currentSize += nextSize; downloadedFileIds.push(_importFile.id); - requestModule.get(url, function (res) { + requestModule.get(url, (res) => { const contentType = res.headers['content-type']; if (!details.type && contentType) { details.type = contentType; diff --git a/apps/meteor/app/importer-pending-files/server/index.ts b/apps/meteor/app/importer-pending-files/server/index.ts index 9a8117ea8099..2486e6b0f0d9 100644 --- a/apps/meteor/app/importer-pending-files/server/index.ts +++ b/apps/meteor/app/importer-pending-files/server/index.ts @@ -1,5 +1,5 @@ -import { PendingFileImporter } from './importer'; import { Importers } from '../../importer/server'; +import { PendingFileImporter } from './importer'; import { PendingFileImporterInfo } from './info'; Importers.add(new PendingFileImporterInfo(), PendingFileImporter); diff --git a/apps/meteor/app/importer-slack-users/server/importer.js b/apps/meteor/app/importer-slack-users/server/importer.js index 855b46106b4b..177885b7833a 100644 --- a/apps/meteor/app/importer-slack-users/server/importer.js +++ b/apps/meteor/app/importer-slack-users/server/importer.js @@ -2,8 +2,8 @@ import fs from 'fs'; import { Settings } from '@rocket.chat/models'; -import { Base, ProgressStep } from '../../importer/server'; import { RocketChatFile } from '../../file/server'; +import { Base, ProgressStep } from '../../importer/server'; export class SlackUsersImporter extends Base { constructor(info, importRecord, converterOptions = {}) { diff --git a/apps/meteor/app/importer-slack-users/server/index.ts b/apps/meteor/app/importer-slack-users/server/index.ts index 1651465e5d4e..6d23f6939b13 100644 --- a/apps/meteor/app/importer-slack-users/server/index.ts +++ b/apps/meteor/app/importer-slack-users/server/index.ts @@ -1,5 +1,5 @@ -import { SlackUsersImporter } from './importer'; import { Importers } from '../../importer/server'; import { SlackUsersImporterInfo } from '../lib/info'; +import { SlackUsersImporter } from './importer'; Importers.add(new SlackUsersImporterInfo(), SlackUsersImporter); diff --git a/apps/meteor/app/importer-slack/server/importer.js b/apps/meteor/app/importer-slack/server/importer.js index ddb430cbd5fc..a5b0da6e549d 100644 --- a/apps/meteor/app/importer-slack/server/importer.js +++ b/apps/meteor/app/importer-slack/server/importer.js @@ -1,9 +1,9 @@ -import _ from 'underscore'; import { Messages, Settings, ImportData } from '@rocket.chat/models'; +import _ from 'underscore'; import { Base, ProgressStep, ImporterWebsocket } from '../../importer/server'; -import { settings } from '../../settings/server'; import { MentionsParser } from '../../mentions/lib/MentionsParser'; +import { settings } from '../../settings/server'; import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; export class SlackImporter extends Base { diff --git a/apps/meteor/app/importer-slack/server/index.ts b/apps/meteor/app/importer-slack/server/index.ts index d8499b517f00..6442d31dfa42 100644 --- a/apps/meteor/app/importer-slack/server/index.ts +++ b/apps/meteor/app/importer-slack/server/index.ts @@ -1,5 +1,5 @@ -import { SlackImporter } from './importer'; import { Importers } from '../../importer/server'; import { SlackImporterInfo } from '../lib/info'; +import { SlackImporter } from './importer'; Importers.add(new SlackImporterInfo(), SlackImporter); diff --git a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts index b97fdc45efb2..f241879cdc67 100644 --- a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts +++ b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts @@ -1,5 +1,3 @@ -import { Accounts } from 'meteor/accounts-base'; -import { ObjectId } from 'mongodb'; import type { IImportUser, IImportMessage, @@ -14,21 +12,26 @@ import type { IImportRecordType, IMessage as IDBMessage, } from '@rocket.chat/core-typings'; +import type { Logger } from '@rocket.chat/logger'; import { ImportData, Rooms, Users, Subscriptions } from '@rocket.chat/models'; -import { hash as bcryptHash } from 'bcrypt'; -import { SHA256 } from '@rocket.chat/sha256'; import { Random } from '@rocket.chat/random'; +import { SHA256 } from '@rocket.chat/sha256'; +import { hash as bcryptHash } from 'bcrypt'; +import { Accounts } from 'meteor/accounts-base'; +import { ObjectId } from 'mongodb'; -import type { IConversionCallbacks } from '../definitions/IConversionCallbacks'; -import { generateUsernameSuggestion, insertMessage, saveUserIdentity, addUserToDefaultChannels } from '../../../lib/server'; -import { setUserActiveStatus } from '../../../lib/server/functions/setUserActiveStatus'; -import type { Logger } from '../../../../server/lib/logger/Logger'; -import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; +import { callbacks } from '../../../../lib/callbacks'; +import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; import { saveRoomSettings } from '../../../channel-settings/server/methods/saveRoomSettings'; -import { createPrivateGroupMethod } from '../../../lib/server/methods/createPrivateGroup'; +import { addUserToDefaultChannels } from '../../../lib/server/functions/addUserToDefaultChannels'; +import { generateUsernameSuggestion } from '../../../lib/server/functions/getUsernameSuggestion'; +import { insertMessage } from '../../../lib/server/functions/insertMessage'; +import { saveUserIdentity } from '../../../lib/server/functions/saveUserIdentity'; +import { setUserActiveStatus } from '../../../lib/server/functions/setUserActiveStatus'; import { createChannelMethod } from '../../../lib/server/methods/createChannel'; -import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; -import { callbacks } from '../../../../lib/callbacks'; +import { createPrivateGroupMethod } from '../../../lib/server/methods/createPrivateGroup'; +import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; +import type { IConversionCallbacks } from '../definitions/IConversionCallbacks'; type IRoom = Record; type IMessage = Record; @@ -150,7 +153,7 @@ export class ImportDataConverter { _id: new ObjectId().toHexString(), data, dataType: type, - ...options, + options, }); } @@ -442,8 +445,6 @@ export class ImportDataConverter { public async convertUsers({ beforeImportFn, afterImportFn, onErrorFn, afterBatchFn }: IConversionCallbacks = {}): Promise { const users = (await this.getUsersToImport()) as IImportUserRecord[]; - await callbacks.run('beforeUserImport', { userCount: users.length }); - const insertedIds = new Set(); const updatedIds = new Set(); let skippedCount = 0; @@ -451,13 +452,14 @@ export class ImportDataConverter { const batchToInsert = new Set(); - for await (const { data, _id } of users) { + for await (const record of users) { + const { data, _id } = record; if (this.aborted) { break; } try { - if (beforeImportFn && !(await beforeImportFn(data, 'user'))) { + if (beforeImportFn && !(await beforeImportFn(record))) { await this.skipRecord(_id); skippedCount++; continue; @@ -534,7 +536,7 @@ export class ImportDataConverter { } if (afterImportFn) { - await afterImportFn(data, 'user', isNewUser); + await afterImportFn(record, isNewUser); } } catch (e) { this._logger.error(e); @@ -645,7 +647,6 @@ export class ImportDataConverter { const result: Array = []; for await (const importId of mentions) { - // eslint-disable-next-line no-extra-parens if (importId === ('all' as 'string') || importId === 'here') { result.push({ _id: importId, @@ -739,13 +740,14 @@ export class ImportDataConverter { const rids: Array = []; const messages = await this.getMessagesToImport(); - for await (const { data, _id } of messages) { + for await (const record of messages) { + const { data, _id } = record; if (this.aborted) { return; } try { - if (beforeImportFn && !(await beforeImportFn(data, 'message'))) { + if (beforeImportFn && !(await beforeImportFn(record))) { await this.skipRecord(_id); continue; } @@ -814,7 +816,7 @@ export class ImportDataConverter { } if (afterImportFn) { - await afterImportFn(data, 'message', true); + await afterImportFn(record, true); } } catch (e) { await this.saveError(_id, e instanceof Error ? e : new Error(String(e))); @@ -837,7 +839,6 @@ export class ImportDataConverter { async updateRoom(room: IRoom, roomData: IImportChannel, startedByUserId: string): Promise { roomData._id = room._id; - // eslint-disable-next-line no-extra-parens if ((roomData._id as string).toUpperCase() === 'GENERAL' && roomData.name !== room.name) { await saveRoomSettings(startedByUserId, 'GENERAL', 'roomName', roomData.name); } @@ -1115,13 +1116,14 @@ export class ImportDataConverter { async convertChannels(startedByUserId: string, { beforeImportFn, afterImportFn, onErrorFn }: IConversionCallbacks = {}): Promise { const channels = await this.getChannelsToImport(); - for await (const { data, _id } of channels) { + for await (const record of channels) { + const { data, _id } = record; if (this.aborted) { return; } try { - if (beforeImportFn && !(await beforeImportFn(data, 'channel'))) { + if (beforeImportFn && !(await beforeImportFn(record))) { await this.skipRecord(_id); continue; } @@ -1150,7 +1152,7 @@ export class ImportDataConverter { } if (afterImportFn) { - await afterImportFn(data, 'channel', !existingRoom); + await afterImportFn(record, !existingRoom); } } catch (e) { await this.saveError(_id, e instanceof Error ? e : new Error(String(e))); diff --git a/apps/meteor/app/importer/server/classes/ImporterBase.js b/apps/meteor/app/importer/server/classes/ImporterBase.js index 43841af08925..061644130a66 100644 --- a/apps/meteor/app/importer/server/classes/ImporterBase.js +++ b/apps/meteor/app/importer/server/classes/ImporterBase.js @@ -1,14 +1,15 @@ +import { Logger } from '@rocket.chat/logger'; import { Settings, ImportData, Imports } from '@rocket.chat/models'; import AdmZip from 'adm-zip'; -import { Progress } from './ImporterProgress'; -import { ImporterWebsocket } from './ImporterWebsocket'; -import { ProgressStep, ImportPreparingStartedStates } from '../../lib/ImporterProgressStep'; +import { Selection, SelectionChannel, SelectionUser } from '..'; +import { callbacks } from '../../../../lib/callbacks'; +import { t } from '../../../utils/lib/i18n'; import { ImporterInfo } from '../../lib/ImporterInfo'; -import { Logger } from '../../../logger/server'; +import { ProgressStep, ImportPreparingStartedStates } from '../../lib/ImporterProgressStep'; import { ImportDataConverter } from './ImportDataConverter'; -import { t } from '../../../utils/lib/i18n'; -import { Selection, SelectionChannel, SelectionUser } from '..'; +import { Progress } from './ImporterProgress'; +import { ImporterWebsocket } from './ImporterWebsocket'; /** * Base class for all of the importers. @@ -96,7 +97,7 @@ export class Base { this.reloadCount(); const started = Date.now(); - const beforeImportFn = async (data, type) => { + const beforeImportFn = async ({ data, dataType: type }) => { if (this.importRecord.valid === false) { this.converter.aborted = true; throw new Error('The import operation is no longer valid.'); @@ -130,7 +131,7 @@ export class Base { } } - return true; + return false; }; const afterImportFn = async () => { @@ -167,6 +168,8 @@ export class Base { await this.applySettingValues({}); await this.updateProgress(ProgressStep.IMPORTING_USERS); + const usersToImport = importSelection.users.filter((user) => user.do_import); + await callbacks.run('beforeUserImport', { userCount: usersToImport.length }); await this.converter.convertUsers({ beforeImportFn, afterImportFn, onErrorFn, afterBatchFn }); await this.updateProgress(ProgressStep.IMPORTING_CHANNELS); diff --git a/apps/meteor/app/importer/server/classes/VirtualDataConverter.ts b/apps/meteor/app/importer/server/classes/VirtualDataConverter.ts index 5fe5147b0f4f..ef850226be5c 100644 --- a/apps/meteor/app/importer/server/classes/VirtualDataConverter.ts +++ b/apps/meteor/app/importer/server/classes/VirtualDataConverter.ts @@ -1,4 +1,3 @@ -import { Random } from '@rocket.chat/random'; import type { IImportUser, IImportUserRecord, @@ -9,6 +8,7 @@ import type { IImportData, IImportChannel, } from '@rocket.chat/core-typings'; +import { Random } from '@rocket.chat/random'; import { ImportDataConverter } from './ImportDataConverter'; import type { IConverterOptions } from './ImportDataConverter'; @@ -56,8 +56,8 @@ export class VirtualDataConverter extends ImportDataConverter { return undefined; } - public addUserSync(data: IImportUser): void { - return this.addObjectSync('user', data); + public addUserSync(data: IImportUser, options?: Record): void { + return this.addObjectSync('user', data, options); } protected async addObject(type: IImportRecordType, data: IImportData, options: Record = {}): Promise { @@ -79,7 +79,7 @@ export class VirtualDataConverter extends ImportDataConverter { _id: Random.id(), data, dataType: type, - ...options, + options, }); } diff --git a/apps/meteor/app/importer/server/definitions/IConversionCallbacks.ts b/apps/meteor/app/importer/server/definitions/IConversionCallbacks.ts index 27d0c38b69d3..262970dd60d9 100644 --- a/apps/meteor/app/importer/server/definitions/IConversionCallbacks.ts +++ b/apps/meteor/app/importer/server/definitions/IConversionCallbacks.ts @@ -1,11 +1,11 @@ -import type { IImportUser, IImportMessage, IImportChannel, IImportRecordType } from '@rocket.chat/core-typings'; +import type { IImportRecord } from '@rocket.chat/core-typings'; -type ImporterBeforeImportCallback = { - (data: IImportUser | IImportChannel | IImportMessage, type: IImportRecordType): Promise; +export type ImporterBeforeImportCallback = { + (data: IImportRecord): Promise; }; export type ImporterAfterImportCallback = { - (data: IImportUser | IImportChannel | IImportMessage, type: IImportRecordType, isNewRecord: boolean): Promise; + (data: IImportRecord, isNewRecord: boolean): Promise; }; export interface IConversionCallbacks { diff --git a/apps/meteor/app/importer/server/index.ts b/apps/meteor/app/importer/server/index.ts index 319344a343c5..8d7d872455c0 100644 --- a/apps/meteor/app/importer/server/index.ts +++ b/apps/meteor/app/importer/server/index.ts @@ -1,11 +1,11 @@ +import { ImporterInfo } from '../lib/ImporterInfo'; +import { ProgressStep } from '../lib/ImporterProgressStep'; +import { Importers } from '../lib/Importers'; import { Base } from './classes/ImporterBase'; -import { ImporterWebsocket } from './classes/ImporterWebsocket'; import { Selection } from './classes/ImporterSelection'; import { SelectionChannel } from './classes/ImporterSelectionChannel'; import { SelectionUser } from './classes/ImporterSelectionUser'; -import { ProgressStep } from '../lib/ImporterProgressStep'; -import { Importers } from '../lib/Importers'; -import { ImporterInfo } from '../lib/ImporterInfo'; +import { ImporterWebsocket } from './classes/ImporterWebsocket'; import './methods'; import './startup/setImportsToInvalid'; import './startup/store'; diff --git a/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts b/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts index d075e3ea5ca6..dc92901435ea 100644 --- a/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts +++ b/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts @@ -1,20 +1,20 @@ +import fs from 'fs'; import http from 'http'; import https from 'https'; -import fs from 'fs'; -import { Meteor } from 'meteor/meteor'; import { Import } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { RocketChatImportFileInstance } from '../startup/store'; -import { ProgressStep } from '../../lib/ImporterProgressStep'; +import { Importers } from '..'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Importers } from '../../lib/Importers'; +import { ProgressStep } from '../../lib/ImporterProgressStep'; +import { RocketChatImportFileInstance } from '../startup/store'; function downloadHttpFile(fileUrl: string, writeStream: fs.WriteStream): void { const protocol = fileUrl.startsWith('https') ? https : http; - protocol.get(fileUrl, function (response) { + protocol.get(fileUrl, (response) => { response.pipe(writeStream); }); } diff --git a/apps/meteor/app/importer/server/methods/getImportFileData.ts b/apps/meteor/app/importer/server/methods/getImportFileData.ts index caad5ff78750..cf81e3074da5 100644 --- a/apps/meteor/app/importer/server/methods/getImportFileData.ts +++ b/apps/meteor/app/importer/server/methods/getImportFileData.ts @@ -1,15 +1,15 @@ -import path from 'path'; import fs from 'fs'; +import path from 'path'; -import { Meteor } from 'meteor/meteor'; import type { IImportFileData } from '@rocket.chat/core-typings'; import { Imports } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { RocketChatImportFileInstance } from '../startup/store'; +import { Importers } from '..'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { ProgressStep } from '../../lib/ImporterProgressStep'; -import { Importers } from '..'; +import { RocketChatImportFileInstance } from '../startup/store'; export const executeGetImportFileData = async (): Promise => { const operation = await Imports.findLastImport(); diff --git a/apps/meteor/app/importer/server/methods/getImportProgress.ts b/apps/meteor/app/importer/server/methods/getImportProgress.ts index f5b8509e619a..cdce1da395d7 100644 --- a/apps/meteor/app/importer/server/methods/getImportProgress.ts +++ b/apps/meteor/app/importer/server/methods/getImportProgress.ts @@ -1,10 +1,10 @@ import type { IImportProgress } from '@rocket.chat/core-typings'; -import { Meteor } from 'meteor/meteor'; import { Imports } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Importers } from '..'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; export const executeGetImportProgress = async (): Promise => { const operation = await Imports.findLastImport(); diff --git a/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts b/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts index 1283481c2103..edf021a19eac 100644 --- a/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts +++ b/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import type { IImport } from '@rocket.chat/core-typings'; import { Imports } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IImport } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/importer/server/methods/startImport.ts b/apps/meteor/app/importer/server/methods/startImport.ts index 347e3d3fa3b4..fda135b4fba7 100644 --- a/apps/meteor/app/importer/server/methods/startImport.ts +++ b/apps/meteor/app/importer/server/methods/startImport.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import type { StartImportParamsPOST } from '@rocket.chat/rest-typings'; +import type { IUser } from '@rocket.chat/core-typings'; import { Imports } from '@rocket.chat/models'; +import type { StartImportParamsPOST } from '@rocket.chat/rest-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IUser } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Importers, Selection, SelectionChannel, SelectionUser } from '..'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; export const executeStartImport = async ({ input }: StartImportParamsPOST, startedByUserId: IUser['_id']) => { const operation = await Imports.findLastImport(); diff --git a/apps/meteor/app/importer/server/methods/uploadImportFile.ts b/apps/meteor/app/importer/server/methods/uploadImportFile.ts index ebe888b55931..68955a102b94 100644 --- a/apps/meteor/app/importer/server/methods/uploadImportFile.ts +++ b/apps/meteor/app/importer/server/methods/uploadImportFile.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; import { Import } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { RocketChatFile } from '../../../file/server'; -import { RocketChatImportFileInstance } from '../startup/store'; +import { Importers } from '..'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { RocketChatFile } from '../../../file/server'; import { ProgressStep } from '../../lib/ImporterProgressStep'; -import { Importers } from '..'; +import { RocketChatImportFileInstance } from '../startup/store'; export const executeUploadImportFile = async ( userId: IUser['_id'], diff --git a/apps/meteor/app/importer/server/startup/setImportsToInvalid.js b/apps/meteor/app/importer/server/startup/setImportsToInvalid.js index 5e915dbdb992..bca3061efea0 100644 --- a/apps/meteor/app/importer/server/startup/setImportsToInvalid.js +++ b/apps/meteor/app/importer/server/startup/setImportsToInvalid.js @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Imports } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { ProgressStep } from '../../lib/ImporterProgressStep'; -Meteor.startup(async function () { +Meteor.startup(async () => { const lastOperation = await Imports.findLastImport(); // If the operation is still on "ready to start" state, we don't need to invalidate it. diff --git a/apps/meteor/app/importer/server/startup/store.js b/apps/meteor/app/importer/server/startup/store.js index f28b7e5363eb..4cdd3baea2ab 100644 --- a/apps/meteor/app/importer/server/startup/store.js +++ b/apps/meteor/app/importer/server/startup/store.js @@ -5,7 +5,7 @@ import { settings } from '../../../settings/server'; export let RocketChatImportFileInstance; -Meteor.startup(function () { +Meteor.startup(() => { const RocketChatStore = RocketChatFile.FileSystem; let path = '/tmp/rocketchat-importer'; diff --git a/apps/meteor/app/integrations/server/api/api.js b/apps/meteor/app/integrations/server/api/api.js index 3456a7aba4c4..5162fa54ad9c 100644 --- a/apps/meteor/app/integrations/server/api/api.js +++ b/apps/meteor/app/integrations/server/api/api.js @@ -1,114 +1,21 @@ -import { VM, VMScript } from 'vm2'; +import { Integrations, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import { Livechat } from 'meteor/rocketchat:livechat'; import _ from 'underscore'; -import moment from 'moment'; -import { Integrations, Users } from '@rocket.chat/models'; -import * as Models from '@rocket.chat/models'; -import * as s from '../../../../lib/utils/stringUtils'; -import { incomingLogger } from '../logger'; -import { processWebhookMessage } from '../../../lib/server/functions/processWebhookMessage'; import { API, APIClass, defaultRateLimiterOptions } from '../../../api/server'; +import { processWebhookMessage } from '../../../lib/server/functions/processWebhookMessage'; import { settings } from '../../../settings/server'; -import { httpCall } from '../../../../server/lib/http/call'; -import { deleteOutgoingIntegration } from '../methods/outgoing/deleteOutgoingIntegration'; -import { deasyncPromise } from '../../../../server/deasync/deasync'; +import { IsolatedVMScriptEngine } from '../lib/isolated-vm/isolated-vm'; +import { VM2ScriptEngine } from '../lib/vm2/vm2'; +import { incomingLogger } from '../logger'; import { addOutgoingIntegration } from '../methods/outgoing/addOutgoingIntegration'; +import { deleteOutgoingIntegration } from '../methods/outgoing/deleteOutgoingIntegration'; -const DISABLE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.DISABLE_INTEGRATION_SCRIPTS).toLowerCase()); - -export const forbiddenModelMethods = ['registerModel', 'getCollectionName']; - -const compiledScripts = {}; -function buildSandbox(store = {}) { - const httpAsync = async (method, url, options) => { - try { - return { - result: await httpCall(method, url, options), - }; - } catch (error) { - return { error }; - } - }; - - const sandbox = { - scriptTimeout(reject) { - return setTimeout(() => reject('timed out'), 3000); - }, - _, - s, - console, - moment, - Promise, - Livechat, - Store: { - set(key, val) { - store[key] = val; - return val; - }, - get(key) { - return store[key]; - }, - }, - HTTP: (method, url, options) => { - // TODO: deprecate, track and alert - return deasyncPromise(httpAsync(method, url, options)); - }, - // TODO: Export fetch as the non deprecated method - }; - Object.keys(Models) - .filter((k) => !forbiddenModelMethods.includes(k)) - .forEach((k) => { - sandbox[k] = Models[k]; - }); - return { store, sandbox }; -} - -function getIntegrationScript(integration) { - if (DISABLE_INTEGRATION_SCRIPTS) { - throw API.v1.failure('integration-scripts-disabled'); - } - - const compiledScript = compiledScripts[integration._id]; - if (compiledScript && +compiledScript._updatedAt === +integration._updatedAt) { - return compiledScript.script; - } - - const script = integration.scriptCompiled; - const { sandbox, store } = buildSandbox(); - try { - incomingLogger.info({ msg: 'Will evaluate script of Trigger', integration: integration.name }); - incomingLogger.debug(script); - - const vmScript = new VMScript(`${script}; Script;`, 'script.js'); - const vm = new VM({ - sandbox, - }); - - const ScriptClass = vm.run(vmScript); - - if (ScriptClass) { - compiledScripts[integration._id] = { - script: new ScriptClass(), - store, - _updatedAt: integration._updatedAt, - }; +const vm2Engine = new VM2ScriptEngine(true); +const ivmEngine = new IsolatedVMScriptEngine(true); - return compiledScripts[integration._id].script; - } - } catch (err) { - incomingLogger.error({ - msg: 'Error evaluating Script in Trigger', - integration: integration.name, - script, - err, - }); - throw API.v1.failure('error-evaluating-script'); - } - - incomingLogger.error({ msg: 'Class "Script" not in Trigger', integration: integration.name }); - throw API.v1.failure('class-script-not-found'); +function getEngine(integration) { + return integration.scriptEngine === 'isolated-vm' ? ivmEngine : vm2Engine; } async function createIntegration(options, user) { @@ -178,20 +85,9 @@ async function executeIntegrationRest() { emoji: this.integration.emoji, }; - if ( - !DISABLE_INTEGRATION_SCRIPTS && - this.integration.scriptEnabled && - this.integration.scriptCompiled && - this.integration.scriptCompiled.trim() !== '' - ) { - let script; - try { - script = getIntegrationScript(this.integration); - } catch (e) { - incomingLogger.error(e); - return API.v1.failure(e.message); - } + const scriptEngine = getEngine(this.integration); + if (scriptEngine.integrationHasValidScript(this.integration)) { this.request.setEncoding('utf8'); const content_raw = this.request.read(); @@ -216,37 +112,12 @@ async function executeIntegrationRest() { }, }; - try { - const { sandbox } = buildSandbox(compiledScripts[this.integration._id].store); - sandbox.script = script; - sandbox.request = request; - - const vm = new VM({ - timeout: 3000, - sandbox, - }); - - const result = await new Promise((resolve, reject) => { - process.nextTick(async () => { - try { - const scriptResult = await vm.run(` - new Promise((resolve, reject) => { - scriptTimeout(reject); - try { - resolve(script.process_incoming_request({ request: request })); - } catch(e) { - reject(e); - } - }).catch((error) => { throw new Error(error); }); - `); - - resolve(scriptResult); - } catch (e) { - reject(e); - } - }); - }); + const result = await scriptEngine.processIncomingRequest({ + integration: this.integration, + request, + }); + try { if (!result) { incomingLogger.debug({ msg: 'Process Incoming Request result of Trigger has no data', diff --git a/apps/meteor/app/integrations/server/lib/ScriptEngine.ts b/apps/meteor/app/integrations/server/lib/ScriptEngine.ts new file mode 100644 index 000000000000..e46984a893ef --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/ScriptEngine.ts @@ -0,0 +1,385 @@ +import type { + IUser, + IRoom, + IMessage, + IOutgoingIntegration, + IIncomingIntegration, + IIntegration, + IIntegrationHistory, +} from '@rocket.chat/core-typings'; +import type { Logger } from '@rocket.chat/logger'; +import type { serverFetch } from '@rocket.chat/server-fetch'; +import { wrapExceptions } from '@rocket.chat/tools'; + +import { incomingLogger, outgoingLogger } from '../logger'; +import type { IScriptClass, CompiledScript } from './definition'; +import { updateHistory } from './updateHistory'; + +type OutgoingRequestBaseData = { + token: IOutgoingIntegration['token']; + bot: false; + trigger_word: string; +}; + +type OutgoingRequestSendMessageData = OutgoingRequestBaseData & { + channel_id: string; + channel_name: string; + message_id: string; + timestamp: Date; + user_id: string; + user_name: string; + text: string; + siteUrl: string; + alias?: string; + bot?: boolean; + isEdited?: true; + tmid?: string; +}; + +type OutgoingRequestUploadedFileData = OutgoingRequestBaseData & { + channel_id: string; + channel_name: string; + message_id: string; + timestamp: Date; + user_id: string; + user_name: string; + text: string; + + user: IUser; + room: IRoom; + message: IMessage; + + alias?: string; + bot?: boolean; +}; + +type OutgoingRequestRoomCreatedData = OutgoingRequestBaseData & { + channel_id: string; + channel_name: string; + timestamp: Date; + user_id: string; + user_name: string; + owner: IUser; + room: IRoom; +}; + +type OutgoingRequestRoomData = OutgoingRequestBaseData & { + channel_id: string; + channel_name: string; + timestamp: Date; + user_id: string; + user_name: string; + owner: IUser; + room: IRoom; + bot?: boolean; +}; + +type OutgoingRequestUserCreatedData = OutgoingRequestBaseData & { + timestamp: Date; + user_id: string; + user_name: string; + user: IUser; + bot?: boolean; +}; + +type OutgoingRequestData = + | OutgoingRequestSendMessageData + | OutgoingRequestUploadedFileData + | OutgoingRequestRoomCreatedData + | OutgoingRequestRoomData + | OutgoingRequestUserCreatedData; + +type OutgoingRequest = { + params: Record; + method: 'POST'; + url: string; + data: OutgoingRequestData; + auth: undefined; + headers: Record; +}; + +type OutgoingRequestFromScript = { + url?: string; + headers?: Record; + method?: string; + message?: { + text?: string; + channel?: string; + attachments?: { + color?: string; + author_name?: string; + author_link?: string; + author_icon?: string; + title?: string; + title_link?: string; + text?: string; + fields?: { + title?: string; + value?: string; + short?: boolean; + }[]; + image_url?: string; + thumb_url?: string; + }[]; + }; + + auth?: string; + data?: Record; +}; + +type OutgoingRequestContext = { + integration: IOutgoingIntegration; + data: OutgoingRequestData; + historyId: IIntegrationHistory['_id']; + url: string; +}; + +type ProcessedOutgoingRequest = OutgoingRequest | OutgoingRequestFromScript; + +type OutgoingResponseContext = { + integration: IOutgoingIntegration; + request: ProcessedOutgoingRequest; + response: Awaited>; + content: string; + historyId: IIntegrationHistory['_id']; +}; + +type IncomingIntegrationRequest = { + url: { + hash: string | null | undefined; + search: string | null | undefined; + query: Record; + pathname: string | null | undefined; + path: string | null | undefined; + }; + url_raw: string; + url_params: Record; + content: Record; + content_raw: string; + headers: Record; + body: Record; + user: Pick, '_id' | 'name' | 'username'>; +}; + +export abstract class IntegrationScriptEngine { + protected compiledScripts: Record; + + public get disabled(): boolean { + return this.isDisabled(); + } + + public get incoming(): IsIncoming { + return this.isIncoming; + } + + constructor(private isIncoming: IsIncoming) { + this.compiledScripts = {}; + } + + public integrationHasValidScript(integration: IIntegration): boolean { + return Boolean(!this.disabled && integration.scriptEnabled && integration.scriptCompiled && integration.scriptCompiled.trim() !== ''); + } + + // PrepareOutgoingRequest will execute a script to build the request object that will be used for the actual integration request + // It may also return a message object to be sent to the room where the integration was triggered + public async prepareOutgoingRequest({ integration, data, historyId, url }: OutgoingRequestContext): Promise { + const request: OutgoingRequest = { + params: {}, + method: 'POST', + url, + data, + auth: undefined, + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36', + }, + }; + + if (!(await this.hasScriptAndMethod(integration, 'prepare_outgoing_request'))) { + return request; + } + + return this.executeOutgoingScript(integration, 'prepare_outgoing_request', { request }, historyId); + } + + public async processOutgoingResponse({ + integration, + request, + response, + content, + historyId, + }: OutgoingResponseContext): Promise { + if (!(await this.hasScriptAndMethod(integration, 'process_outgoing_response'))) { + return; + } + + const sandbox = { + request, + response: { + error: null, + status_code: response.status, + content, + content_raw: content, + headers: Object.fromEntries(response.headers), + }, + }; + + const scriptResult = await this.executeOutgoingScript(integration, 'process_outgoing_response', sandbox, historyId); + + if (scriptResult === false) { + return scriptResult; + } + + if (scriptResult?.content) { + return scriptResult.content; + } + } + + public async processIncomingRequest({ + integration, + request, + }: { + integration: IIncomingIntegration; + request: IncomingIntegrationRequest; + }): Promise { + return this.executeIncomingScript(integration, 'process_incoming_request', { request }); + } + + protected get logger(): ReturnType { + if (this.isIncoming) { + return incomingLogger; + } + + return outgoingLogger; + } + + protected async executeOutgoingScript( + integration: IOutgoingIntegration, + method: keyof IScriptClass, + params: Record, + historyId: IIntegrationHistory['_id'], + ): Promise { + if (this.disabled) { + return; + } + + const script = await wrapExceptions(() => this.getIntegrationScript(integration)).suppress((e: any) => + updateHistory({ + historyId, + step: 'execute-script-getting-script', + error: true, + errorStack: e, + }), + ); + + if (!script) { + return; + } + + if (!script[method]) { + this.logger.error(`Method "${method}" not found in the Integration "${integration.name}"`); + await updateHistory({ historyId, step: `execute-script-no-method-${method}` }); + return; + } + + try { + await updateHistory({ historyId, step: `execute-script-before-running-${method}` }); + + const result = await this.runScriptMethod({ + integrationId: integration._id, + script, + method, + params, + }); + + this.logger.debug({ + msg: `Script method "${method}" result of the Integration "${integration.name}" is:`, + result, + }); + + return result; + } catch (err: any) { + await updateHistory({ + historyId, + step: `execute-script-error-running-${method}`, + error: true, + errorStack: err.stack.replace(/^/gm, ' '), + }); + this.logger.error({ + msg: 'Error running Script in the Integration', + integration: integration.name, + err, + }); + this.logger.debug({ + msg: 'Error running Script in the Integration', + integration: integration.name, + script: integration.scriptCompiled, + }); + } + } + + protected async executeIncomingScript( + integration: IIncomingIntegration, + method: keyof IScriptClass, + params: Record, + ): Promise { + if (!this.integrationHasValidScript(integration)) { + return; + } + + const script = await wrapExceptions(() => this.getIntegrationScript(integration)).catch((e) => { + this.logger.error(e); + throw e; + }); + + if (!script[method]) { + this.logger.error(`Method "${method}" not found in the Integration "${integration.name}"`); + return; + } + + return wrapExceptions(() => + this.runScriptMethod({ + integrationId: integration._id, + script, + method, + params, + }), + ).catch((err: any) => { + this.logger.error({ + msg: 'Error running Script in Trigger', + integration: integration.name, + script: integration.scriptCompiled, + err, + }); + throw new Error('error-running-script'); + }); + } + + protected async hasScriptAndMethod(integration: IIntegration, method: keyof IScriptClass): Promise { + const script = await this.getScriptSafely(integration); + return typeof script?.[method] === 'function'; + } + + protected async getScriptSafely(integration: IIntegration): Promise | undefined> { + if (this.disabled || integration.scriptEnabled !== true || !integration.scriptCompiled || integration.scriptCompiled.trim() === '') { + return; + } + + return wrapExceptions(() => this.getIntegrationScript(integration)).suppress(); + } + + protected abstract isDisabled(): boolean; + + protected abstract runScriptMethod({ + integrationId, + script, + method, + params, + }: { + integrationId: IIntegration['_id']; + script: IScriptClass; + method: keyof IScriptClass; + params: Record; + }): Promise; + + protected abstract getIntegrationScript(integration: IIntegration): Promise>; +} diff --git a/apps/meteor/app/integrations/server/lib/definition.ts b/apps/meteor/app/integrations/server/lib/definition.ts new file mode 100644 index 000000000000..b4d11b9f4e8b --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/definition.ts @@ -0,0 +1,19 @@ +import type { IIntegration } from '@rocket.chat/core-typings'; + +export interface IScriptClass { + prepare_outgoing_request?: (params: Record) => any; + process_outgoing_response?: (params: Record) => any; + process_incoming_request?: (params: Record) => any; +} + +export type FullScriptClass = Required; + +export type CompiledScript = { + script: Partial; + store: Record; + _updatedAt: IIntegration['_updatedAt']; +}; + +export type CompatibilityScriptResult = IScriptClass & { + availableFunctions: (keyof IScriptClass)[]; +}; diff --git a/apps/meteor/app/integrations/server/lib/isolated-vm/buildSandbox.ts b/apps/meteor/app/integrations/server/lib/isolated-vm/buildSandbox.ts new file mode 100644 index 000000000000..1bbefb6a2ee7 --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/isolated-vm/buildSandbox.ts @@ -0,0 +1,127 @@ +import { EventEmitter } from 'events'; + +import { serverFetch as fetch, Response } from '@rocket.chat/server-fetch'; +import ivm, { type Context } from 'isolated-vm'; + +import * as s from '../../../../../lib/utils/stringUtils'; + +const proxyObject = (obj: Record, forbiddenKeys: string[] = []): Record => { + return copyObject({ + isProxy: true, + get: (key: string) => { + if (forbiddenKeys.includes(key)) { + return undefined; + } + + const value = obj[key]; + + if (typeof value === 'function') { + return new ivm.Reference(async (...args: any[]) => { + const result = (obj[key] as any)(...args); + + if (result && result instanceof Promise) { + return new Promise(async (resolve, reject) => { + try { + const awaitedResult = await result; + resolve(makeTransferable(awaitedResult)); + } catch (e) { + reject(e); + } + }); + } + + return makeTransferable(result); + }); + } + + return makeTransferable(value); + }, + }); +}; + +const copyObject = (obj: Record | any[]): Record | any[] => { + if (Array.isArray(obj)) { + return obj.map((data) => copyData(data)); + } + + if (obj instanceof Response) { + return proxyObject(obj, ['clone']); + } + + if (isSemiTransferable(obj)) { + return obj; + } + + if (typeof obj[Symbol.iterator as any] === 'function') { + return copyObject(Array.from(obj as any)); + } + + if (obj instanceof EventEmitter) { + return {}; + } + + const keys = Object.keys(obj); + + return { + ...Object.fromEntries( + keys.map((key) => { + const data = obj[key]; + + if (typeof data === 'function') { + return [key, new ivm.Callback((...args: any[]) => obj[key](...args))]; + } + + return [key, copyData(data)]; + }), + ), + }; +}; + +// Transferable data can be passed to isolates directly +const isTransferable = (data: any): data is ivm.Transferable => { + const dataType = typeof data; + + if (data === ivm) { + return true; + } + + if (['null', 'undefined', 'string', 'number', 'boolean', 'function'].includes(dataType)) { + return true; + } + + if (dataType !== 'object') { + return false; + } + + return ( + data instanceof ivm.Isolate || + data instanceof ivm.Context || + data instanceof ivm.Script || + data instanceof ivm.ExternalCopy || + data instanceof ivm.Callback || + data instanceof ivm.Reference + ); +}; + +// Semi-transferable data can be copied with an ivm.ExternalCopy without needing any manipulation. +const isSemiTransferable = (data: any) => data instanceof ArrayBuffer; + +const copyData = | any[]>(data: T) => (isTransferable(data) ? data : copyObject(data)); +const makeTransferable = (data: any) => (isTransferable(data) ? data : new ivm.ExternalCopy(copyObject(data)).copyInto()); + +export const buildSandbox = (context: Context) => { + const { global: jail } = context; + jail.setSync('global', jail.derefInto()); + jail.setSync('ivm', ivm); + + jail.setSync('s', makeTransferable(s)); + jail.setSync('console', makeTransferable(console)); + + jail.setSync( + 'serverFetch', + new ivm.Reference(async (url: string, ...args: any[]) => { + const result = await fetch(url, ...args); + return makeTransferable(result); + }), + ); +}; diff --git a/apps/meteor/app/integrations/server/lib/isolated-vm/getCompatibilityScript.ts b/apps/meteor/app/integrations/server/lib/isolated-vm/getCompatibilityScript.ts new file mode 100644 index 000000000000..77ce2475e8c2 --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/isolated-vm/getCompatibilityScript.ts @@ -0,0 +1,60 @@ +export const getCompatibilityScript = (customScript?: string) => ` + const Store = (function() { + const store = {}; + return { + set(key, val) { + store[key] = val; + return val; + }, + get(key) { + return store[key]; + }, + }; + })(); + + const reproxy = (reference) => { + return new Proxy(reference, { + get(target, p, receiver) { + if (target !== reference || p === 'then') { + return Reflect.get(target, p, receiver); + } + + const data = reference.get(p); + + if (typeof data === 'object' && data instanceof ivm.Reference && data.typeof === 'function') { + return (...args) => data.apply(undefined, args, { arguments: { copy: true }, result: { promise: true } }); + } + + return data; + } + }); + }; + + //url, options, allowSelfSignedCertificate + const fetch = async (...args) => { + const result = await serverFetch.apply(undefined, args, { arguments: { copy: true }, result: { promise: true } }); + + if (result && typeof result === 'object' && result.isProxy) { + return reproxy(result); + } + + return result; + }; + + ${customScript} + + (function() { + const instance = new Script(); + + const functions = { + ...(typeof instance['prepare_outgoing_request'] === 'function' ? { prepare_outgoing_request : (...args) => instance.prepare_outgoing_request(...args) } : {}), + ...(typeof instance['process_outgoing_response'] === 'function' ? { process_outgoing_response : (...args) => instance.process_outgoing_response(...args) } : {}), + ...(typeof instance['process_incoming_request'] === 'function' ? { process_incoming_request : (...args) => instance.process_incoming_request(...args) } : {}), + }; + + return { + ...functions, + availableFunctions: Object.keys(functions), + } + })(); +`; diff --git a/apps/meteor/app/integrations/server/lib/isolated-vm/isolated-vm.ts b/apps/meteor/app/integrations/server/lib/isolated-vm/isolated-vm.ts new file mode 100644 index 000000000000..2c78b6d98a7c --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/isolated-vm/isolated-vm.ts @@ -0,0 +1,99 @@ +import type { IIntegration, ValueOf } from '@rocket.chat/core-typings'; +import { pick } from '@rocket.chat/tools'; +import ivm, { type Reference } from 'isolated-vm'; + +import { IntegrationScriptEngine } from '../ScriptEngine'; +import type { IScriptClass, CompatibilityScriptResult, FullScriptClass } from '../definition'; +import { buildSandbox } from './buildSandbox'; +import { getCompatibilityScript } from './getCompatibilityScript'; + +const DISABLE_INTEGRATION_SCRIPTS = ['yes', 'true', 'ivm'].includes(String(process.env.DISABLE_INTEGRATION_SCRIPTS).toLowerCase()); + +export class IsolatedVMScriptEngine extends IntegrationScriptEngine { + protected isDisabled(): boolean { + return DISABLE_INTEGRATION_SCRIPTS; + } + + protected async callScriptFunction( + scriptReference: Reference>, + ...params: Parameters> + ): Promise { + return scriptReference.applySync(undefined, params, { + arguments: { copy: true }, + result: { copy: true, promise: true }, + }); + } + + protected async runScriptMethod({ + script, + method, + params, + }: { + integrationId: IIntegration['_id']; + script: Partial; + method: keyof IScriptClass; + params: Record; + }): Promise { + const fn = script[method]; + + if (typeof fn !== 'function') { + throw new Error('integration-method-not-found'); + } + + return fn(params); + } + + protected async getIntegrationScript(integration: IIntegration): Promise> { + if (this.disabled) { + throw new Error('integration-scripts-disabled'); + } + + const compiledScript = this.compiledScripts[integration._id]; + if (compiledScript && +compiledScript._updatedAt === +integration._updatedAt) { + return compiledScript.script; + } + + const script = integration.scriptCompiled; + try { + this.logger.info({ msg: 'Will evaluate the integration script', integration: pick(integration, 'name', '_id') }); + this.logger.debug(script); + + const isolate = new ivm.Isolate({ memoryLimit: 8 }); + + const ivmScript = await isolate.compileScript(getCompatibilityScript(script)); + + const ivmContext = isolate.createContextSync(); + buildSandbox(ivmContext); + + const ivmResult: Reference = await ivmScript.run(ivmContext, { + reference: true, + timeout: 3000, + }); + + const availableFunctions = await ivmResult.get('availableFunctions', { copy: true }); + const scriptFunctions = Object.fromEntries( + availableFunctions.map((functionName) => { + const fnReference = ivmResult.getSync(functionName, { reference: true }); + return [functionName, (...params: Parameters>) => this.callScriptFunction(fnReference, ...params)]; + }), + ) as Partial; + + this.compiledScripts[integration._id] = { + script: scriptFunctions, + store: {}, + _updatedAt: integration._updatedAt, + }; + + return scriptFunctions; + } catch (err: any) { + this.logger.error({ + msg: 'Error evaluating integration script', + integration: integration.name, + script, + err, + }); + + throw new Error('error-evaluating-script'); + } + } +} diff --git a/apps/meteor/app/integrations/server/lib/mountQueriesBasedOnPermission.ts b/apps/meteor/app/integrations/server/lib/mountQueriesBasedOnPermission.ts index 9f07262e781c..a72d131d0e8f 100644 --- a/apps/meteor/app/integrations/server/lib/mountQueriesBasedOnPermission.ts +++ b/apps/meteor/app/integrations/server/lib/mountQueriesBasedOnPermission.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import type { DeepWritable } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import type { Filter } from 'mongodb'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/integrations/server/lib/triggerHandler.js b/apps/meteor/app/integrations/server/lib/triggerHandler.js index 9988bfd06da5..b5050b8c4716 100644 --- a/apps/meteor/app/integrations/server/lib/triggerHandler.js +++ b/apps/meteor/app/integrations/server/lib/triggerHandler.js @@ -1,30 +1,25 @@ -import { VM, VMScript } from 'vm2'; +import { Integrations, Users, Rooms, Messages } from '@rocket.chat/models'; +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { wrapExceptions } from '@rocket.chat/tools'; import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; import _ from 'underscore'; -import moment from 'moment'; -import { Integrations, IntegrationHistory, Users, Rooms, Messages } from '@rocket.chat/models'; -import * as Models from '@rocket.chat/models'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import * as s from '../../../../lib/utils/stringUtils'; -import { settings } from '../../../settings/server'; import { getRoomByNameOrIdWithOptionToJoin } from '../../../lib/server/functions/getRoomByNameOrIdWithOptionToJoin'; import { processWebhookMessage } from '../../../lib/server/functions/processWebhookMessage'; -import { outgoingLogger } from '../logger'; +import { settings } from '../../../settings/server'; import { outgoingEvents } from '../../lib/outgoingEvents'; -import { omit } from '../../../../lib/utils/omit'; -import { forbiddenModelMethods } from '../api/api'; -import { httpCall } from '../../../../server/lib/http/call'; -import { deasyncPromise } from '../../../../server/deasync/deasync'; - -const DISABLE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.DISABLE_INTEGRATION_SCRIPTS).toLowerCase()); +import { outgoingLogger } from '../logger'; +import { IsolatedVMScriptEngine } from './isolated-vm/isolated-vm'; +import { updateHistory } from './updateHistory'; +import { VM2ScriptEngine } from './vm2/vm2'; class RocketChatIntegrationHandler { constructor() { this.successResults = [200, 201, 202]; this.compiledScripts = {}; this.triggers = {}; + this.vm2Engine = new VM2ScriptEngine(false); + this.ivmEngine = new IsolatedVMScriptEngine(false); } addIntegration(record) { @@ -51,6 +46,10 @@ class RocketChatIntegrationHandler { } } + getEngine(integration) { + return integration.scriptEngine === 'isolated-vm' ? this.ivmEngine : this.vm2Engine; + } + removeIntegration(record) { for (const trigger of Object.values(this.triggers)) { delete trigger[record._id]; @@ -67,114 +66,6 @@ class RocketChatIntegrationHandler { return false; } - async updateHistory({ - historyId, - step, - integration, - event, - data, - triggerWord, - ranPrepareScript, - prepareSentMessage, - processSentMessage, - resultMessage, - finished, - url, - httpCallData, - httpError, - httpResult, - error, - errorStack, - }) { - const history = { - type: 'outgoing-webhook', - step, - }; - - // Usually is only added on initial insert - if (integration) { - history.integration = integration; - } - - // Usually is only added on initial insert - if (event) { - history.event = event; - } - - if (data) { - history.data = { ...data }; - - if (data.user) { - history.data.user = omit(data.user, 'services'); - } - - if (data.room) { - history.data.room = data.room; - } - } - - if (triggerWord) { - history.triggerWord = triggerWord; - } - - if (typeof ranPrepareScript !== 'undefined') { - history.ranPrepareScript = ranPrepareScript; - } - - if (prepareSentMessage) { - history.prepareSentMessage = prepareSentMessage; - } - - if (processSentMessage) { - history.processSentMessage = processSentMessage; - } - - if (resultMessage) { - history.resultMessage = resultMessage; - } - - if (typeof finished !== 'undefined') { - history.finished = finished; - } - - if (url) { - history.url = url; - } - - if (typeof httpCallData !== 'undefined') { - history.httpCallData = httpCallData; - } - - if (httpError) { - history.httpError = httpError; - } - - if (typeof httpResult !== 'undefined') { - history.httpResult = JSON.stringify(httpResult, null, 2); - } - - if (typeof error !== 'undefined') { - history.error = error; - } - - if (typeof errorStack !== 'undefined') { - history.errorStack = errorStack; - } - - if (historyId) { - await IntegrationHistory.updateOne({ _id: historyId }, { $set: history }); - return historyId; - } - - history._createdAt = new Date(); - - const _id = Random.id(); - - await IntegrationHistory.insertOne({ _id, ...history }); - - return _id; - } - // Trigger is the trigger, nameOrId is a string which is used to try and find a room, room is a room, message is a message, and data contains "user_name" if trigger.impersonateUser is truthful. async sendMessage({ trigger, nameOrId = '', room, message, data }) { let user; @@ -229,199 +120,6 @@ class RocketChatIntegrationHandler { return message; } - buildSandbox(store = {}) { - const httpAsync = async (method, url, options) => { - try { - return { - result: await httpCall(method, url, options), - }; - } catch (error) { - return { error }; - } - }; - - const sandbox = { - scriptTimeout(reject) { - return setTimeout(() => reject('timed out'), 3000); - }, - _, - s, - console, - moment, - Promise, - Store: { - set: (key, val) => { - store[key] = val; - }, - get: (key) => store[key], - }, - HTTP: (method, url, options) => { - // TODO: deprecate, track and alert - return deasyncPromise(httpAsync(method, url, options)); - }, - // TODO: Export fetch as the non deprecated method - }; - - Object.keys(Models) - .filter((k) => !forbiddenModelMethods.includes(k)) - .forEach((k) => { - sandbox[k] = Models[k]; - }); - - return { store, sandbox }; - } - - getIntegrationScript(integration) { - if (DISABLE_INTEGRATION_SCRIPTS) { - throw new Meteor.Error('integration-scripts-disabled'); - } - - const compiledScript = this.compiledScripts[integration._id]; - if (compiledScript && +compiledScript._updatedAt === +integration._updatedAt) { - return compiledScript.script; - } - - const script = integration.scriptCompiled; - const { store, sandbox } = this.buildSandbox(); - - try { - outgoingLogger.info({ msg: 'Will evaluate script of Trigger', integration: integration.name }); - outgoingLogger.debug(script); - - const vmScript = new VMScript(`${script}; Script;`, 'script.js'); - const vm = new VM({ - sandbox, - }); - - const ScriptClass = vm.run(vmScript); - - if (ScriptClass) { - this.compiledScripts[integration._id] = { - script: new ScriptClass(), - store, - _updatedAt: integration._updatedAt, - }; - - return this.compiledScripts[integration._id].script; - } - } catch (err) { - outgoingLogger.error({ - msg: 'Error evaluating Script in Trigger', - integration: integration.name, - script, - err, - }); - throw new Meteor.Error('error-evaluating-script'); - } - - outgoingLogger.error(`Class "Script" not in Trigger ${integration.name}:`); - throw new Meteor.Error('class-script-not-found'); - } - - hasScriptAndMethod(integration, method) { - if ( - DISABLE_INTEGRATION_SCRIPTS || - integration.scriptEnabled !== true || - !integration.scriptCompiled || - integration.scriptCompiled.trim() === '' - ) { - return false; - } - - let script; - try { - script = this.getIntegrationScript(integration); - } catch (e) { - return false; - } - - return typeof script[method] !== 'undefined'; - } - - async executeScript(integration, method, params, historyId) { - if (DISABLE_INTEGRATION_SCRIPTS) { - return; - } - - let script; - try { - script = this.getIntegrationScript(integration); - } catch (e) { - await this.updateHistory({ - historyId, - step: 'execute-script-getting-script', - error: true, - errorStack: e, - }); - return; - } - - if (!script[method]) { - outgoingLogger.error(`Method "${method}" no found in the Integration "${integration.name}"`); - await this.updateHistory({ historyId, step: `execute-script-no-method-${method}` }); - return; - } - - try { - const { sandbox } = this.buildSandbox(this.compiledScripts[integration._id].store); - sandbox.script = script; - sandbox.method = method; - sandbox.params = params; - - await this.updateHistory({ historyId, step: `execute-script-before-running-${method}` }); - - const vm = new VM({ - timeout: 3000, - sandbox, - }); - - const result = await new Promise((resolve, reject) => { - process.nextTick(async () => { - try { - const scriptResult = await vm.run(` - new Promise((resolve, reject) => { - scriptTimeout(reject); - try { - resolve(script[method](params)) - } catch(e) { - reject(e); - } - }).catch((error) => { throw new Error(error); }); - `); - - resolve(scriptResult); - } catch (e) { - reject(e); - } - }); - }); - - outgoingLogger.debug({ - msg: `Script method "${method}" result of the Integration "${integration.name}" is:`, - result, - }); - - return result; - } catch (err) { - await this.updateHistory({ - historyId, - step: `execute-script-error-running-${method}`, - error: true, - errorStack: err.stack.replace(/^/gm, ' '), - }); - outgoingLogger.error({ - msg: 'Error running Script in the Integration', - integration: integration.name, - err, - }); - outgoingLogger.debug({ - msg: 'Error running Script in the Integration', - integration: integration.name, - script: integration.scriptCompiled, - }); // Only output the compiled script if debugging is enabled, so the logs don't get spammed. - } - } - eventNameArgumentsToObject(...args) { const argObject = { event: args[0], @@ -680,6 +378,17 @@ class RocketChatIntegrationHandler { } } + // Ensure that any errors thrown by the script engine will contibue to be compatible with Meteor.Error + async wrapScriptEngineCall(getter) { + return wrapExceptions(getter).catch((error) => { + if (error instanceof Error) { + throw new Meteor.Error(error.message); + } + + throw error; + }); + } + async executeTriggerUrl(url, trigger, { event, message, room, owner, user }, theHistoryId, tries = 0) { if (!this.isTriggerEnabled(trigger)) { outgoingLogger.warn(`The trigger "${trigger.name}" is no longer enabled, stopping execution of it at try: ${tries}`); @@ -715,7 +424,7 @@ class RocketChatIntegrationHandler { return; } - const historyId = await this.updateHistory({ + const historyId = await updateHistory({ step: 'start-execute-trigger-url', integration: trigger, event, @@ -731,36 +440,32 @@ class RocketChatIntegrationHandler { } this.mapEventArgsToData(data, { trigger, event, message, room, owner, user }); - await this.updateHistory({ historyId, step: 'mapped-args-to-data', data, triggerWord: word }); + await updateHistory({ historyId, step: 'mapped-args-to-data', data, triggerWord: word }); outgoingLogger.info(`Will be executing the Integration "${trigger.name}" to the url: ${url}`); outgoingLogger.debug({ data }); - let opts = { - params: {}, - method: 'POST', - url, - data, - auth: undefined, - headers: { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36', - }, - }; + const scriptEngine = this.getEngine(trigger); - if (this.hasScriptAndMethod(trigger, 'prepare_outgoing_request')) { - opts = await this.executeScript(trigger, 'prepare_outgoing_request', { request: opts }, historyId); - } + const opts = await this.wrapScriptEngineCall(() => + scriptEngine.prepareOutgoingRequest({ + integration: trigger, + data, + url, + historyId, + }), + ); - await this.updateHistory({ historyId, step: 'after-maybe-ran-prepare', ranPrepareScript: true }); + await updateHistory({ historyId, step: 'after-maybe-ran-prepare', ranPrepareScript: true }); if (!opts) { - await this.updateHistory({ historyId, step: 'after-prepare-no-opts', finished: true }); + await updateHistory({ historyId, step: 'after-prepare-no-opts', finished: true }); return; } if (opts.message) { const prepareMessage = await this.sendMessage({ trigger, room, message: opts.message, data }); - await this.updateHistory({ + await updateHistory({ historyId, step: 'after-prepare-send-message', prepareSentMessage: prepareMessage, @@ -768,7 +473,7 @@ class RocketChatIntegrationHandler { } if (!opts.url || !opts.method) { - await this.updateHistory({ historyId, step: 'after-prepare-no-url_or_method', finished: true }); + await updateHistory({ historyId, step: 'after-prepare-no-url_or_method', finished: true }); return; } @@ -782,7 +487,7 @@ class RocketChatIntegrationHandler { opts.headers.Authorization = `Basic ${base64}`; } - await this.updateHistory({ + await updateHistory({ historyId, step: 'pre-http-call', url: opts.url, @@ -823,47 +528,42 @@ class RocketChatIntegrationHandler { } })(); - await this.updateHistory({ + await updateHistory({ historyId, step: 'after-http-call', httpError: null, httpResult: content, }); - if (this.hasScriptAndMethod(trigger, 'process_outgoing_response')) { - const sandbox = { + const responseContent = await this.wrapScriptEngineCall(() => + scriptEngine.processOutgoingResponse({ + integration: trigger, request: opts, - response: { - error: null, - status_code: res.status, // These values will be undefined to close issues #4175, #5762, and #5896 - content, - content_raw: content, - headers: Object.fromEntries(res.headers), - }, - }; - - const scriptResult = await this.executeScript(trigger, 'process_outgoing_response', sandbox, historyId); - - if (scriptResult && scriptResult.content) { - const resultMessage = await this.sendMessage({ - trigger, - room, - message: scriptResult.content, - data, - }); - await this.updateHistory({ - historyId, - step: 'after-process-send-message', - processSentMessage: resultMessage, - finished: true, - }); - return; - } + response: res, + content, + historyId, + }), + ); + + if (responseContent) { + const resultMessage = await this.sendMessage({ + trigger, + room, + message: responseContent, + data, + }); + await updateHistory({ + historyId, + step: 'after-process-send-message', + processSentMessage: resultMessage, + finished: true, + }); + return; + } - if (scriptResult === false) { - await this.updateHistory({ historyId, step: 'after-process-false-result', finished: true }); - return; - } + if (responseContent === false) { + await updateHistory({ historyId, step: 'after-process-false-result', finished: true }); + return; } // if the result contained nothing or wasn't a successful statusCode @@ -875,14 +575,14 @@ class RocketChatIntegrationHandler { }); if (res.status === 410) { - await this.updateHistory({ historyId, step: 'after-process-http-status-410', error: true }); + await updateHistory({ historyId, step: 'after-process-http-status-410', error: true }); outgoingLogger.error(`Disabling the Integration "${trigger.name}" because the status code was 401 (Gone).`); await Integrations.updateOne({ _id: trigger._id }, { $set: { enabled: false } }); return; } if (res.status === 500) { - await this.updateHistory({ historyId, step: 'after-process-http-status-500', error: true }); + await updateHistory({ historyId, step: 'after-process-http-status-500', error: true }); outgoingLogger.error({ msg: `Error "500" for the Integration "${trigger.name}" to ${url}.`, content, @@ -893,7 +593,7 @@ class RocketChatIntegrationHandler { if (trigger.retryFailedCalls) { if (tries < trigger.retryCount && trigger.retryDelay) { - await this.updateHistory({ historyId, error: true, step: `going-to-retry-${tries + 1}` }); + await updateHistory({ historyId, error: true, step: `going-to-retry-${tries + 1}` }); let waitTime; @@ -912,7 +612,7 @@ class RocketChatIntegrationHandler { break; default: const er = new Error("The integration's retryDelay setting is invalid."); - await this.updateHistory({ + await updateHistory({ historyId, step: 'failed-and-retry-delay-is-invalid', error: true, @@ -926,10 +626,10 @@ class RocketChatIntegrationHandler { void this.executeTriggerUrl(url, trigger, { event, message, room, owner, user }, historyId, tries + 1); }, waitTime); } else { - await this.updateHistory({ historyId, step: 'too-many-retries', error: true }); + await updateHistory({ historyId, step: 'too-many-retries', error: true }); } } else { - await this.updateHistory({ + await updateHistory({ historyId, step: 'failed-and-not-configured-to-retry', error: true, @@ -943,7 +643,7 @@ class RocketChatIntegrationHandler { if (content && this.successResults.includes(res.status)) { if (data?.text || data?.attachments) { const resultMsg = await this.sendMessage({ trigger, room, message: data, data }); - await this.updateHistory({ + await updateHistory({ historyId, step: 'url-response-sent-message', resultMessage: resultMsg, @@ -954,7 +654,7 @@ class RocketChatIntegrationHandler { }) .catch(async (error) => { outgoingLogger.error(error); - await this.updateHistory({ + await updateHistory({ historyId, step: 'after-http-call', httpError: error, diff --git a/apps/meteor/app/integrations/server/lib/updateHistory.ts b/apps/meteor/app/integrations/server/lib/updateHistory.ts new file mode 100644 index 000000000000..9f7a3017108d --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/updateHistory.ts @@ -0,0 +1,96 @@ +import type { IIntegrationHistory, IIntegration, IMessage, AtLeast } from '@rocket.chat/core-typings'; +import { IntegrationHistory } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; + +import { omit } from '../../../../lib/utils/omit'; + +export const updateHistory = async ({ + historyId, + step, + integration, + event, + data, + triggerWord, + ranPrepareScript, + prepareSentMessage, + processSentMessage, + resultMessage, + finished, + url, + httpCallData, + httpError, + httpResult, + error, + errorStack, +}: { + historyId: IIntegrationHistory['_id']; + step: IIntegrationHistory['step']; + integration?: IIntegration; + event?: string; + triggerWord?: string; + ranPrepareScript?: boolean; + prepareSentMessage?: { channel: string; message: Partial }[]; + processSentMessage?: { channel: string; message: Partial }[]; + resultMessage?: { channel: string; message: Partial }[]; + finished?: boolean; + url?: string; + httpCallData?: Record; // ProcessedOutgoingRequest.data + httpError?: any; // null or whatever error type `fetch` may throw + httpResult?: string | null; + + error?: boolean; + errorStack?: any; // Error | Error['stack'] + + data?: Record; +}) => { + const { user: userData, room: roomData, ...fullData } = data || {}; + + const history: AtLeast = { + type: 'outgoing-webhook', + step, + + // Usually is only added on initial insert + ...(integration ? { integration } : {}), + // Usually is only added on initial insert + ...(event ? { event } : {}), + ...(fullData + ? { + data: { + ...fullData, + ...(userData ? { user: omit(userData, 'services') } : {}), + ...(roomData ? { room: roomData } : {}), + }, + } + : {}), + ...(triggerWord ? { triggerWord } : {}), + ...(typeof ranPrepareScript !== 'undefined' ? { ranPrepareScript } : {}), + ...(prepareSentMessage ? { prepareSentMessage } : {}), + ...(processSentMessage ? { processSentMessage } : {}), + ...(resultMessage ? { resultMessage } : {}), + ...(typeof finished !== 'undefined' ? { finished } : {}), + ...(url ? { url } : {}), + ...(typeof httpCallData !== 'undefined' ? { httpCallData } : {}), + ...(httpError ? { httpError } : {}), + ...(typeof httpResult !== 'undefined' ? { httpResult: JSON.stringify(httpResult, null, 2) } : {}), + ...(typeof error !== 'undefined' ? { error } : {}), + ...(typeof errorStack !== 'undefined' ? { errorStack } : {}), + }; + + if (historyId) { + await IntegrationHistory.updateOne({ _id: historyId }, { $set: history }); + return historyId; + } + + // Can't create a new history without there being an integration + if (!history.integration) { + throw new Error('error-invalid-integration'); + } + + history._createdAt = new Date(); + + const _id = Random.id(); + + await IntegrationHistory.insertOne({ _id, ...history } as IIntegrationHistory); + + return _id; +}; diff --git a/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts b/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts index 2068db0b57e1..398f81161279 100644 --- a/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/lib/validateOutgoingIntegration.ts @@ -1,19 +1,18 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { Babel } from 'meteor/babel-compiler'; -import _ from 'underscore'; import type { IUser, INewOutgoingIntegration, IOutgoingIntegration, IUpdateOutgoingIntegration } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import { pick } from '@rocket.chat/tools'; +import { Babel } from 'meteor/babel-compiler'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { parseCSV } from '../../../../lib/utils/parseCSV'; import { hasPermissionAsync, hasAllPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { outgoingEvents } from '../../lib/outgoingEvents'; -import { parseCSV } from '../../../../lib/utils/parseCSV'; +import { isScriptEngineFrozen } from './validateScriptEngine'; const scopedChannels = ['all_public_channels', 'all_private_groups', 'all_direct_messages']; const validChannelChars = ['@', '#']; -const FREEZE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.FREEZE_INTEGRATION_SCRIPTS).toLowerCase()); - function _verifyRequiredFields(integration: INewOutgoingIntegration | IUpdateOutgoingIntegration): void { if ( !integration.event || @@ -152,6 +151,7 @@ export const validateOutgoingIntegration = async function ( const integrationData: IOutgoingIntegration = { ...integration, + scriptEngine: integration.scriptEngine ?? 'isolated-vm', type: 'webhook-outgoing', channel: channels, userId: user._id, @@ -171,7 +171,13 @@ export const validateOutgoingIntegration = async function ( delete integrationData.triggerWords; } - if (!FREEZE_INTEGRATION_SCRIPTS && integration.scriptEnabled === true && integration.script && integration.script.trim() !== '') { + // Only compile the script if it is enabled and using a sandbox that is not frozen + if ( + !isScriptEngineFrozen(integrationData.scriptEngine) && + integration.scriptEnabled === true && + integration.script && + integration.script.trim() !== '' + ) { try { const babelOptions = Object.assign(Babel.getDefaultOptions({ runtime: false }), { compact: true, @@ -183,7 +189,7 @@ export const validateOutgoingIntegration = async function ( integrationData.scriptError = undefined; } catch (e) { integrationData.scriptCompiled = undefined; - integrationData.scriptError = e instanceof Error ? _.pick(e, 'name', 'message', 'stack') : undefined; + integrationData.scriptError = e instanceof Error ? pick(e, 'name', 'message', 'stack') : undefined; } } diff --git a/apps/meteor/app/integrations/server/lib/validateScriptEngine.ts b/apps/meteor/app/integrations/server/lib/validateScriptEngine.ts new file mode 100644 index 000000000000..c20dc9c59427 --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/validateScriptEngine.ts @@ -0,0 +1,26 @@ +import type { IntegrationScriptEngine } from '@rocket.chat/core-typings'; +import { wrapExceptions } from '@rocket.chat/tools'; + +const FREEZE_INTEGRATION_SCRIPTS_VALUE = String(process.env.FREEZE_INTEGRATION_SCRIPTS).toLowerCase(); +const FREEZE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(FREEZE_INTEGRATION_SCRIPTS_VALUE); + +export const validateScriptEngine = (engine?: IntegrationScriptEngine) => { + if (FREEZE_INTEGRATION_SCRIPTS) { + throw new Error('integration-scripts-disabled'); + } + + const engineCode = engine === 'isolated-vm' ? 'ivm' : 'vm2'; + + if (engineCode === FREEZE_INTEGRATION_SCRIPTS_VALUE) { + if (engineCode === 'ivm') { + throw new Error('integration-scripts-isolated-vm-disabled'); + } + + throw new Error('integration-scripts-vm2-disabled'); + } + + return true; +}; + +export const isScriptEngineFrozen = (engine?: IntegrationScriptEngine) => + wrapExceptions(() => !validateScriptEngine(engine)).catch(() => true); diff --git a/apps/meteor/app/integrations/server/lib/vm2/buildSandbox.ts b/apps/meteor/app/integrations/server/lib/vm2/buildSandbox.ts new file mode 100644 index 000000000000..9ba74404cf26 --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/vm2/buildSandbox.ts @@ -0,0 +1,88 @@ +import * as Models from '@rocket.chat/models'; +import moment from 'moment'; +import _ from 'underscore'; + +import * as s from '../../../../../lib/utils/stringUtils'; +import { deasyncPromise } from '../../../../../server/deasync/deasync'; +import { httpCall } from '../../../../../server/lib/http/call'; + +const forbiddenModelMethods: readonly (keyof typeof Models)[] = ['registerModel', 'getCollectionName']; + +type ModelName = Exclude; + +export type Vm2Sandbox = { + scriptTimeout: (reject: (reason?: any) => void) => ReturnType; + _: typeof _; + s: typeof s; + console: typeof console; + moment: typeof moment; + Promise: typeof Promise; + Store: { + set: IsIncoming extends true ? (key: string, value: any) => any : (key: string, value: any) => void; + get: (key: string) => any; + }; + HTTP: (method: string, url: string, options: Record) => unknown; +} & (IsIncoming extends true ? { Livechat: undefined } : never) & + Record; + +export const buildSandbox = ( + store: Record, + isIncoming?: IsIncoming, +): { + store: Record; + sandbox: Vm2Sandbox; +} => { + const httpAsync = async (method: string, url: string, options: Record) => { + try { + return { + result: await httpCall(method, url, options), + }; + } catch (error) { + return { error }; + } + }; + + const sandbox = { + scriptTimeout(reject: (reason?: any) => void) { + return setTimeout(() => reject('timed out'), 3000); + }, + _, + s, + console, + moment, + Promise, + // There's a small difference between the sandbox that is sent to incoming and to outgoing scripts + // Technically we could unify this but since we're deprecating vm2 anyway I'm keeping this old behavior here until the feature is removed completely + ...(isIncoming + ? { + Livechat: undefined, + Store: { + set: (key: string, val: any): any => { + store[key] = val; + return val; + }, + get: (key: string) => store[key], + }, + } + : { + Store: { + set: (key: string, val: any): void => { + store[key] = val; + }, + get: (key: string) => store[key], + }, + }), + HTTP: (method: string, url: string, options: Record) => { + // TODO: deprecate, track and alert + return deasyncPromise(httpAsync(method, url, options)); + }, + } as Vm2Sandbox; + + (Object.keys(Models) as ModelName[]) + .filter((k) => !forbiddenModelMethods.includes(k)) + .forEach((k) => { + sandbox[k] = Models[k]; + }); + + return { store, sandbox }; +}; diff --git a/apps/meteor/app/integrations/server/lib/vm2/vm2.ts b/apps/meteor/app/integrations/server/lib/vm2/vm2.ts new file mode 100644 index 000000000000..5f7519d69346 --- /dev/null +++ b/apps/meteor/app/integrations/server/lib/vm2/vm2.ts @@ -0,0 +1,111 @@ +import type { IIntegration } from '@rocket.chat/core-typings'; +import { VM, VMScript } from 'vm2'; + +import { IntegrationScriptEngine } from '../ScriptEngine'; +import type { IScriptClass } from '../definition'; +import { buildSandbox, type Vm2Sandbox } from './buildSandbox'; + +const DISABLE_INTEGRATION_SCRIPTS = ['yes', 'true', 'vm2'].includes(String(process.env.DISABLE_INTEGRATION_SCRIPTS).toLowerCase()); + +export class VM2ScriptEngine extends IntegrationScriptEngine { + protected isDisabled(): boolean { + return DISABLE_INTEGRATION_SCRIPTS; + } + + protected buildSandbox(store: Record = {}): { store: Record; sandbox: Vm2Sandbox } { + return buildSandbox(store, this.incoming); + } + + protected async runScriptMethod({ + integrationId, + script, + method, + params, + }: { + integrationId: IIntegration['_id']; + script: IScriptClass; + method: keyof IScriptClass; + params: Record; + }): Promise { + const { sandbox } = this.buildSandbox(this.compiledScripts[integrationId].store); + + const vm = new VM({ + timeout: 3000, + sandbox: { + ...sandbox, + script, + method, + params, + ...(this.incoming && 'request' in params ? { request: params.request } : {}), + }, + }); + + return new Promise((resolve, reject) => { + process.nextTick(async () => { + try { + const scriptResult = await vm.run(` + new Promise((resolve, reject) => { + scriptTimeout(reject); + try { + resolve(script[method](params)) + } catch(e) { + reject(e); + } + }).catch((error) => { throw new Error(error); }); + `); + + resolve(scriptResult); + } catch (e) { + reject(e); + } + }); + }); + } + + protected async getIntegrationScript(integration: IIntegration): Promise> { + if (this.disabled) { + throw new Error('integration-scripts-disabled'); + } + + const compiledScript = this.compiledScripts[integration._id]; + if (compiledScript && +compiledScript._updatedAt === +integration._updatedAt) { + return compiledScript.script; + } + + const script = integration.scriptCompiled; + const { store, sandbox } = this.buildSandbox(); + + try { + this.logger.info({ msg: 'Will evaluate script of Trigger', integration: integration.name }); + this.logger.debug(script); + + const vmScript = new VMScript(`${script}; Script;`, 'script.js'); + const vm = new VM({ + sandbox, + }); + + const ScriptClass = vm.run(vmScript); + + if (ScriptClass) { + this.compiledScripts[integration._id] = { + script: new ScriptClass(), + store, + _updatedAt: integration._updatedAt, + }; + + return this.compiledScripts[integration._id].script; + } + } catch (err) { + this.logger.error({ + msg: 'Error evaluating Script in Trigger', + integration: integration.name, + script, + err, + }); + throw new Error('error-evaluating-script'); + } + + this.logger.error({ msg: 'Class "Script" not in Trigger', integration: integration.name }); + throw new Error('class-script-not-found'); + } +} diff --git a/apps/meteor/app/integrations/server/logger.ts b/apps/meteor/app/integrations/server/logger.ts index a1b90ea8c101..47da6bcf22c5 100644 --- a/apps/meteor/app/integrations/server/logger.ts +++ b/apps/meteor/app/integrations/server/logger.ts @@ -1,4 +1,4 @@ -import { Logger } from '../../logger/server'; +import { Logger } from '@rocket.chat/logger'; const logger = new Logger('Integrations'); diff --git a/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts b/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts index 28ea23333d64..2447683bd291 100644 --- a/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts +++ b/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import notifications from '../../../notifications/server/lib/Notifications'; diff --git a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts index 96e2fa280920..45548a17a565 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts @@ -1,18 +1,17 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { Random } from '@rocket.chat/random'; -import { Babel } from 'meteor/babel-compiler'; -import _ from 'underscore'; import type { INewIncomingIntegration, IIncomingIntegration } from '@rocket.chat/core-typings'; import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Babel } from 'meteor/babel-compiler'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; import { hasPermissionAsync, hasAllPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; +import { validateScriptEngine, isScriptEngineFrozen } from '../../lib/validateScriptEngine'; const validChannelChars = ['@', '#']; -const FREEZE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.FREEZE_INTEGRATION_SCRIPTS).toLowerCase()); - declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { @@ -32,7 +31,8 @@ export const addIncomingIntegration = async (userId: string, integration: INewIn alias: Match.Maybe(String), emoji: Match.Maybe(String), scriptEnabled: Boolean, - overrideDestinationChannelEnabled: Boolean, + scriptEngine: Match.Maybe(String), + overrideDestinationChannelEnabled: Match.Maybe(Boolean), script: Match.Maybe(String), avatar: Match.Maybe(String), }), @@ -76,8 +76,8 @@ export const addIncomingIntegration = async (userId: string, integration: INewIn }); } - if (FREEZE_INTEGRATION_SCRIPTS && integration.script?.trim()) { - throw new Meteor.Error('integration-scripts-disabled'); + if (integration.script?.trim()) { + validateScriptEngine(integration.scriptEngine ?? 'isolated-vm'); } const user = await Users.findOne({ username: integration.username }); @@ -90,15 +90,23 @@ export const addIncomingIntegration = async (userId: string, integration: INewIn const integrationData: IIncomingIntegration = { ...integration, + scriptEngine: integration.scriptEngine ?? 'isolated-vm', type: 'webhook-incoming', channel: channels, + overrideDestinationChannelEnabled: integration.overrideDestinationChannelEnabled ?? false, token: Random.id(48), userId: user._id, _createdAt: new Date(), _createdBy: await Users.findOne({ _id: userId }, { projection: { username: 1 } }), }; - if (integration.scriptEnabled === true && integration.script && integration.script.trim() !== '') { + // Only compile the script if it is enabled and using a sandbox that is not frozen + if ( + !isScriptEngineFrozen(integrationData.scriptEngine) && + integration.scriptEnabled === true && + integration.script && + integration.script.trim() !== '' + ) { try { let babelOptions = Babel.getDefaultOptions({ runtime: false }); babelOptions = _.extend(babelOptions, { compact: true, minified: true, comments: false }); diff --git a/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts index 98d4e9d6fe0a..06fb3e3485e3 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Integrations } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts index cf5ba5de9123..5358e3233ce7 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts @@ -1,16 +1,16 @@ -import { Meteor } from 'meteor/meteor'; -import { Babel } from 'meteor/babel-compiler'; -import _ from 'underscore'; +import type { IIntegration, INewIncomingIntegration, IUpdateIncomingIntegration } from '@rocket.chat/core-typings'; import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import { wrapExceptions } from '@rocket.chat/tools'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IIntegration, INewIncomingIntegration, IUpdateIncomingIntegration } from '@rocket.chat/core-typings'; +import { Babel } from 'meteor/babel-compiler'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; import { hasAllPermissionAsync, hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; +import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateScriptEngine'; const validChannelChars = ['@', '#']; -const FREEZE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.FREEZE_INTEGRATION_SCRIPTS).toLowerCase()); - declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { @@ -66,11 +66,20 @@ Meteor.methods({ }); } - if (FREEZE_INTEGRATION_SCRIPTS) { - if (currentIntegration.script?.trim() !== integration.script?.trim()) { - throw new Meteor.Error('integration-scripts-disabled'); - } - } else { + const oldScriptEngine = currentIntegration.scriptEngine ?? 'vm2'; + const scriptEngine = integration.scriptEngine ?? oldScriptEngine; + if ( + integration.script?.trim() && + (scriptEngine !== oldScriptEngine || integration.script?.trim() !== currentIntegration.script?.trim()) + ) { + wrapExceptions(() => validateScriptEngine(scriptEngine)).catch((e) => { + throw new Meteor.Error(e.message); + }); + } + + const isFrozen = isScriptEngineFrozen(scriptEngine); + + if (!isFrozen) { let scriptCompiled: string | undefined; let scriptError: Pick | undefined; @@ -165,13 +174,16 @@ Meteor.methods({ emoji: integration.emoji, alias: integration.alias, channel: channels, - ...(FREEZE_INTEGRATION_SCRIPTS + ...(isFrozen ? {} : { script: integration.script, scriptEnabled: integration.scriptEnabled, + scriptEngine, }), - overrideDestinationChannelEnabled: integration.overrideDestinationChannelEnabled, + ...(typeof integration.overrideDestinationChannelEnabled !== 'undefined' && { + overrideDestinationChannelEnabled: integration.overrideDestinationChannelEnabled, + }), _updatedAt: new Date(), _updatedBy: await Users.findOne({ _id: this.userId }, { projection: { username: 1 } }), }, diff --git a/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts index aac9b3644dcf..59879f99d475 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts @@ -1,11 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import type { INewOutgoingIntegration, IOutgoingIntegration } from '@rocket.chat/core-typings'; import { Integrations } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { validateOutgoingIntegration } from '../../lib/validateOutgoingIntegration'; +import { validateScriptEngine } from '../../lib/validateScriptEngine'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -14,8 +15,6 @@ declare module '@rocket.chat/ui-contexts' { } } -const FREEZE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.FREEZE_INTEGRATION_SCRIPTS).toLowerCase()); - export const addOutgoingIntegration = async (userId: string, integration: INewOutgoingIntegration): Promise => { check( integration, @@ -29,6 +28,7 @@ export const addOutgoingIntegration = async (userId: string, integration: INewOu emoji: Match.Maybe(String), scriptEnabled: Boolean, script: Match.Maybe(String), + scriptEngine: Match.Maybe(String), urls: Match.Maybe([String]), event: Match.Maybe(String), triggerWords: Match.Maybe([String]), @@ -52,8 +52,8 @@ export const addOutgoingIntegration = async (userId: string, integration: INewOu throw new Meteor.Error('not_authorized'); } - if (FREEZE_INTEGRATION_SCRIPTS && integration.script?.trim()) { - throw new Meteor.Error('integration-scripts-disabled'); + if (integration.script?.trim()) { + validateScriptEngine(integration.scriptEngine ?? 'isolated-vm'); } const integrationData = await validateOutgoingIntegration(integration, userId); diff --git a/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts index c7b4d4798926..27750bca50f2 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts index 42734d445285..417c308ca6cb 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Integrations, IntegrationHistory } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { triggerHandler } from '../../lib/triggerHandler'; diff --git a/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts index 847976083316..9e62561ebf9a 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts @@ -1,10 +1,12 @@ -import { Meteor } from 'meteor/meteor'; +import type { IIntegration, INewOutgoingIntegration, IUpdateOutgoingIntegration } from '@rocket.chat/core-typings'; import { Integrations, Users } from '@rocket.chat/models'; +import { wrapExceptions } from '@rocket.chat/tools'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IIntegration, INewOutgoingIntegration, IUpdateOutgoingIntegration } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { validateOutgoingIntegration } from '../../lib/validateOutgoingIntegration'; +import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateScriptEngine'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -16,8 +18,6 @@ declare module '@rocket.chat/ui-contexts' { } } -const FREEZE_INTEGRATION_SCRIPTS = ['yes', 'true'].includes(String(process.env.FREEZE_INTEGRATION_SCRIPTS).toLowerCase()); - Meteor.methods({ async updateOutgoingIntegration(integrationId, _integration) { if (!this.userId) { @@ -53,10 +53,19 @@ Meteor.methods({ throw new Meteor.Error('invalid_integration', '[methods] updateOutgoingIntegration -> integration not found'); } - if (FREEZE_INTEGRATION_SCRIPTS && integration.script?.trim() !== currentIntegration.script?.trim()) { - throw new Meteor.Error('integration-scripts-disabled'); + const oldScriptEngine = currentIntegration.scriptEngine ?? 'vm2'; + const scriptEngine = integration.scriptEngine ?? oldScriptEngine; + if ( + integration.script?.trim() && + (scriptEngine !== oldScriptEngine || integration.script?.trim() !== currentIntegration.script?.trim()) + ) { + wrapExceptions(() => validateScriptEngine(scriptEngine)).catch((e) => { + throw new Meteor.Error(e.message); + }); } + const isFrozen = isScriptEngineFrozen(scriptEngine); + await Integrations.updateOne( { _id: integrationId }, { @@ -74,11 +83,12 @@ Meteor.methods({ userId: integration.userId, urls: integration.urls, token: integration.token, - ...(FREEZE_INTEGRATION_SCRIPTS + ...(isFrozen ? {} : { script: integration.script, scriptEnabled: integration.scriptEnabled, + scriptEngine, ...(integration.scriptCompiled ? { scriptCompiled: integration.scriptCompiled } : { scriptError: integration.scriptError }), }), triggerWords: integration.triggerWords, @@ -90,7 +100,7 @@ Meteor.methods({ _updatedAt: new Date(), _updatedBy: await Users.findOne({ _id: this.userId }, { projection: { username: 1 } }), }, - ...(FREEZE_INTEGRATION_SCRIPTS + ...(isFrozen ? {} : { $unset: { diff --git a/apps/meteor/app/integrations/server/startup.ts b/apps/meteor/app/integrations/server/startup.ts index 52fe8445c0b7..7d89fe823a74 100644 --- a/apps/meteor/app/integrations/server/startup.ts +++ b/apps/meteor/app/integrations/server/startup.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import { Integrations } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { triggerHandler } from './lib/triggerHandler'; -Meteor.startup(async function () { +Meteor.startup(async () => { await Integrations.find({ type: 'webhook-outgoing' }).forEach((data) => triggerHandler.addIntegration(data)); }); diff --git a/apps/meteor/app/integrations/server/triggers.ts b/apps/meteor/app/integrations/server/triggers.ts index eedc98f71af4..cdf8acda6a21 100644 --- a/apps/meteor/app/integrations/server/triggers.ts +++ b/apps/meteor/app/integrations/server/triggers.ts @@ -1,4 +1,5 @@ import { callbacks } from '../../../lib/callbacks'; +import { afterLeaveRoomCallback } from '../../../lib/callbacks/afterLeaveRoomCallback'; import { triggerHandler } from './lib/triggerHandler'; const callbackHandler = function _callbackHandler(eventType: string) { @@ -12,6 +13,6 @@ callbacks.add('afterCreateChannel', callbackHandler('roomCreated'), callbacks.pr callbacks.add('afterCreatePrivateGroup', callbackHandler('roomCreated'), callbacks.priority.LOW, 'integrations-roomCreated'); callbacks.add('afterCreateUser', callbackHandler('userCreated'), callbacks.priority.LOW, 'integrations-userCreated'); callbacks.add('afterJoinRoom', callbackHandler('roomJoined'), callbacks.priority.LOW, 'integrations-roomJoined'); -callbacks.add('afterLeaveRoom', callbackHandler('roomLeft'), callbacks.priority.LOW, 'integrations-roomLeft'); +afterLeaveRoomCallback.add(callbackHandler('roomLeft'), callbacks.priority.LOW, 'integrations-roomLeft'); callbacks.add('afterRoomArchived', callbackHandler('roomArchived'), callbacks.priority.LOW, 'integrations-roomArchived'); callbacks.add('afterFileUpload', callbackHandler('fileUploaded'), callbacks.priority.LOW, 'integrations-fileUploaded'); diff --git a/apps/meteor/app/invites/server/functions/findOrCreateInvite.ts b/apps/meteor/app/invites/server/functions/findOrCreateInvite.ts index a1d3487f88ed..052445a1ebc9 100644 --- a/apps/meteor/app/invites/server/functions/findOrCreateInvite.ts +++ b/apps/meteor/app/invites/server/functions/findOrCreateInvite.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; -import { Invites, Subscriptions, Rooms } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; import type { IInvite } from '@rocket.chat/core-typings'; +import { Invites, Subscriptions, Rooms } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import { Meteor } from 'meteor/meteor'; +import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; import { getURL } from '../../../utils/server/getURL'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; function getInviteUrl(invite: Omit) { const { _id } = invite; diff --git a/apps/meteor/app/invites/server/functions/listInvites.ts b/apps/meteor/app/invites/server/functions/listInvites.ts index 21e305ede246..94999a98a542 100644 --- a/apps/meteor/app/invites/server/functions/listInvites.ts +++ b/apps/meteor/app/invites/server/functions/listInvites.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Invites } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/invites/server/functions/removeInvite.ts b/apps/meteor/app/invites/server/functions/removeInvite.ts index 9ccf725c2012..c754d82d071c 100644 --- a/apps/meteor/app/invites/server/functions/removeInvite.ts +++ b/apps/meteor/app/invites/server/functions/removeInvite.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Invites } from '@rocket.chat/models'; import type { IInvite } from '@rocket.chat/core-typings'; +import { Invites } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/invites/server/functions/sendInvitationEmail.ts b/apps/meteor/app/invites/server/functions/sendInvitationEmail.ts index 4f1fd8525633..1c00671ae41d 100644 --- a/apps/meteor/app/invites/server/functions/sendInvitationEmail.ts +++ b/apps/meteor/app/invites/server/functions/sendInvitationEmail.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { Settings } from '@rocket.chat/models'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import * as Mailer from '../../../mailer/server/api'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; let html = ''; diff --git a/apps/meteor/app/invites/server/functions/useInviteToken.ts b/apps/meteor/app/invites/server/functions/useInviteToken.ts index ff26ae6c9a73..21e89bd22fbc 100644 --- a/apps/meteor/app/invites/server/functions/useInviteToken.ts +++ b/apps/meteor/app/invites/server/functions/useInviteToken.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import { Invites, Subscriptions, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { validateInviteToken } from './validateInviteToken'; -import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; +import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; +import { validateInviteToken } from './validateInviteToken'; export const useInviteToken = async (userId: string, token: string) => { if (!userId) { diff --git a/apps/meteor/app/invites/server/functions/validateInviteToken.ts b/apps/meteor/app/invites/server/functions/validateInviteToken.ts index ac12709761e0..0d1727454cab 100644 --- a/apps/meteor/app/invites/server/functions/validateInviteToken.ts +++ b/apps/meteor/app/invites/server/functions/validateInviteToken.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Invites, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; export const validateInviteToken = async (token: string) => { if (!token || typeof token !== 'string') { diff --git a/apps/meteor/app/irc/server/irc-bridge/index.js b/apps/meteor/app/irc/server/irc-bridge/index.js index e83e99c8c3f7..9ab6c47987d0 100644 --- a/apps/meteor/app/irc/server/irc-bridge/index.js +++ b/apps/meteor/app/irc/server/irc-bridge/index.js @@ -1,13 +1,15 @@ -import Queue from 'queue-fifo'; -import moment from 'moment'; +import { Logger } from '@rocket.chat/logger'; import { Settings } from '@rocket.chat/models'; +import moment from 'moment'; +import Queue from 'queue-fifo'; -import * as peerCommandHandlers from './peerHandlers'; -import * as localCommandHandlers from './localHandlers'; import { callbacks } from '../../../../lib/callbacks'; -import * as servers from '../servers'; -import { Logger } from '../../../logger/server'; +import { afterLeaveRoomCallback } from '../../../../lib/callbacks/afterLeaveRoomCallback'; +import { afterLogoutCleanUpCallback } from '../../../../lib/callbacks/afterLogoutCleanUpCallback'; import { withThrottling } from '../../../../lib/utils/highOrderFunctions'; +import * as servers from '../servers'; +import * as localCommandHandlers from './localHandlers'; +import * as peerCommandHandlers from './peerHandlers'; const logger = new Logger('IRC Bridge'); const queueLogger = logger.section('Queue'); @@ -204,7 +206,7 @@ class Bridge { ); callbacks.add('afterJoinRoom', this.onMessageReceived.bind(this, 'local', 'onJoinRoom'), callbacks.priority.LOW, 'irc-on-join-room'); // Leaving rooms or channels - callbacks.add('afterLeaveRoom', this.onMessageReceived.bind(this, 'local', 'onLeaveRoom'), callbacks.priority.LOW, 'irc-on-leave-room'); + afterLeaveRoomCallback.add(this.onMessageReceived.bind(this, 'local', 'onLeaveRoom'), callbacks.priority.LOW, 'irc-on-leave-room'); // Chatting callbacks.add( 'afterSaveMessage', @@ -213,7 +215,7 @@ class Bridge { 'irc-on-save-message', ); // Leaving - callbacks.add('afterLogoutCleanUp', this.onMessageReceived.bind(this, 'local', 'onLogout'), callbacks.priority.LOW, 'irc-on-logout'); + afterLogoutCleanUpCallback.add(this.onMessageReceived.bind(this, 'local', 'onLogout'), callbacks.priority.LOW, 'irc-on-logout'); } removeLocalHandlers() { @@ -222,9 +224,9 @@ class Bridge { callbacks.remove('afterCreateChannel', 'irc-on-create-channel'); callbacks.remove('afterCreateRoom', 'irc-on-create-room'); callbacks.remove('afterJoinRoom', 'irc-on-join-room'); - callbacks.remove('afterLeaveRoom', 'irc-on-leave-room'); + afterLeaveRoomCallback.remove('irc-on-leave-room'); callbacks.remove('afterSaveMessage', 'irc-on-save-message'); - callbacks.remove('afterLogoutCleanUp', 'irc-on-logout'); + afterLogoutCleanUpCallback.remove('irc-on-logout'); } sendCommand(command, parameters) { diff --git a/apps/meteor/app/irc/server/irc-bridge/localHandlers/index.js b/apps/meteor/app/irc/server/irc-bridge/localHandlers/index.js index fcc046ebfcd2..26f3fedf20ca 100644 --- a/apps/meteor/app/irc/server/irc-bridge/localHandlers/index.js +++ b/apps/meteor/app/irc/server/irc-bridge/localHandlers/index.js @@ -1,9 +1,9 @@ import onCreateRoom from './onCreateRoom'; +import onCreateUser from './onCreateUser'; import onJoinRoom from './onJoinRoom'; import onLeaveRoom from './onLeaveRoom'; import onLogin from './onLogin'; import onLogout from './onLogout'; import onSaveMessage from './onSaveMessage'; -import onCreateUser from './onCreateUser'; export { onCreateRoom, onJoinRoom, onLeaveRoom, onLogin, onLogout, onSaveMessage, onCreateUser }; diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js index 0433b8d463be..0968eacc5340 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js @@ -1,6 +1,7 @@ import { Users, Rooms } from '@rocket.chat/models'; -import { createRoom, addUserToRoom } from '../../../../lib/server'; +import { addUserToRoom } from '../../../../lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../../lib/server/functions/createRoom'; // TODO doesn't seem to be used anywhere, remove export default async function handleJoinedChannel(args) { diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js index 8ac835c8084b..9a65f35e5037 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js @@ -1,6 +1,6 @@ import { Users, Rooms } from '@rocket.chat/models'; -import { removeUserFromRoom } from '../../../../lib/server'; +import { removeUserFromRoom } from '../../../../lib/server/functions/removeUserFromRoom'; export default async function handleLeftChannel(args) { const user = await Users.findOne({ diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js index fa975cbabbe2..c8298efab384 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js @@ -1,6 +1,8 @@ import { Users, Rooms } from '@rocket.chat/models'; -import { sendMessage, createDirectRoom } from '../../../../lib/server'; +import { createDirectRoom } from '../../../../lib/server/functions/createDirectRoom'; +import { sendMessage } from '../../../../lib/server/functions/sendMessage'; + /* * * Get direct chat room helper diff --git a/apps/meteor/app/irc/server/irc.js b/apps/meteor/app/irc/server/irc.js index 7012e2829134..2f6efcb99e41 100644 --- a/apps/meteor/app/irc/server/irc.js +++ b/apps/meteor/app/irc/server/irc.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import Bridge from './irc-bridge'; import { settings } from '../../settings/server'; +import Bridge from './irc-bridge'; if (!!settings.get('IRC_Enabled') === true) { // Normalize the config values diff --git a/apps/meteor/app/irc/server/servers/RFC2813/index.js b/apps/meteor/app/irc/server/servers/RFC2813/index.js index ca3b000c4b2f..8c5b8abf19c8 100644 --- a/apps/meteor/app/irc/server/servers/RFC2813/index.js +++ b/apps/meteor/app/irc/server/servers/RFC2813/index.js @@ -1,11 +1,12 @@ +import { EventEmitter } from 'events'; import net from 'net'; import util from 'util'; -import { EventEmitter } from 'events'; +import { Logger } from '@rocket.chat/logger'; + +import localCommandHandlers from './localCommandHandlers'; import parseMessage from './parseMessage'; import peerCommandHandlers from './peerCommandHandlers'; -import localCommandHandlers from './localCommandHandlers'; -import { Logger } from '../../../../logger/server'; const logger = new Logger('IRC Server'); diff --git a/apps/meteor/app/lib/README.md b/apps/meteor/app/lib/README.md index 708a9a21790e..5201c4cadf0e 100644 --- a/apps/meteor/app/lib/README.md +++ b/apps/meteor/app/lib/README.md @@ -47,19 +47,6 @@ settingsRegistry.addGroup('Settings_Group', function() { * `enableQuery` - Only enable this setting if the correspondent setting has the value specified * `alert` - Shows an alert message with the given text -### AccountBox - -You can add items to the left upper corner drop menu: -```javascript -AccountBox.addItem({ - name: 'Livechat', - icon: 'icon-chat-empty', - class: 'livechat-manager', - condition: () => { - return RocketChat.authz.hasPermission('view-livechat-manager'); - } -}); -``` ### Functions n/a diff --git a/apps/meteor/app/lib/client/methods/sendMessage.ts b/apps/meteor/app/lib/client/methods/sendMessage.ts index cd4a91b27333..65da03ac0e6e 100644 --- a/apps/meteor/app/lib/client/methods/sendMessage.ts +++ b/apps/meteor/app/lib/client/methods/sendMessage.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IMessage, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived'; +import { dispatchToastMessage } from '../../../../client/lib/toast'; +import { callbacks } from '../../../../lib/callbacks'; +import { trim } from '../../../../lib/utils/stringUtils'; import { ChatMessage, ChatRoom } from '../../../models/client'; import { settings } from '../../../settings/client'; -import { callbacks } from '../../../../lib/callbacks'; import { t } from '../../../utils/lib/i18n'; -import { dispatchToastMessage } from '../../../../client/lib/toast'; -import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived'; -import { trim } from '../../../../lib/utils/stringUtils'; Meteor.methods({ async sendMessage(message) { @@ -42,7 +42,7 @@ Meteor.methods({ } message = await callbacks.run('beforeSaveMessage', message); - await onClientMessageReceived(message as IMessage).then(function (message) { + await onClientMessageReceived(message as IMessage).then((message) => { ChatMessage.insert(message); return callbacks.run('afterSaveMessage', message, room); }); diff --git a/apps/meteor/app/lib/lib/MessageTypes.ts b/apps/meteor/app/lib/lib/MessageTypes.ts index 8936876a3129..03802ac5b51f 100644 --- a/apps/meteor/app/lib/lib/MessageTypes.ts +++ b/apps/meteor/app/lib/lib/MessageTypes.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import type { MessageTypesValues as MessageTypesValuesType } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { MessageTypes } from '../../ui-utils/lib/MessageTypes'; // import { callbacks } from '../../../lib/callbacks'; -Meteor.startup(function () { +Meteor.startup(() => { MessageTypes.registerType({ id: 'r', system: true, diff --git a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts index 7f3db9315c2c..6dc477a2926f 100644 --- a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts +++ b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts @@ -1,6 +1,6 @@ +import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { Subscriptions, Rooms } from '@rocket.chat/models'; -import { Message } from '@rocket.chat/core-services'; import { callbacks } from '../../../../lib/callbacks'; diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 62f2cf04dece..660af823de9e 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -1,13 +1,13 @@ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; -import { Meteor } from 'meteor/meteor'; +import { Message, Team } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; -import { Message, Team } from '@rocket.chat/core-services'; +import { Meteor } from 'meteor/meteor'; +import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; import { AppEvents, Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; export const addUserToRoom = async function ( rid: string, @@ -107,7 +107,7 @@ export const addUserToRoom = async function ( } if (room.t === 'c' || room.t === 'p') { - process.nextTick(async function () { + process.nextTick(async () => { // Add a new event, with an optional inviter await callbacks.run('afterAddedToRoom', { user: userToBeAdded, inviter }, room); diff --git a/apps/meteor/app/lib/server/functions/archiveRoom.ts b/apps/meteor/app/lib/server/functions/archiveRoom.ts index ad3487465682..01bb882aa144 100644 --- a/apps/meteor/app/lib/server/functions/archiveRoom.ts +++ b/apps/meteor/app/lib/server/functions/archiveRoom.ts @@ -1,6 +1,6 @@ -import { Rooms, Subscriptions } from '@rocket.chat/models'; -import type { IMessage } from '@rocket.chat/core-typings'; import { Message } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; +import { Rooms, Subscriptions } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; diff --git a/apps/meteor/app/lib/server/functions/attachMessage.ts b/apps/meteor/app/lib/server/functions/attachMessage.ts index 105c2c638c46..d7bd45ba01b4 100644 --- a/apps/meteor/app/lib/server/functions/attachMessage.ts +++ b/apps/meteor/app/lib/server/functions/attachMessage.ts @@ -1,9 +1,9 @@ import type { IMessage, IRoom, MessageAttachment } from '@rocket.chat/core-typings'; -import { getUserAvatarURL } from '../../../utils/server/getUserAvatarURL'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { getUserDisplayName } from '../../../../lib/getUserDisplayName'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { settings } from '../../../settings/server/cached'; +import { getUserAvatarURL } from '../../../utils/server/getUserAvatarURL'; export const attachMessage = function ( message: IMessage, diff --git a/apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts b/apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts index 0f51f78f3c76..316a3b8779a0 100644 --- a/apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts +++ b/apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Team } from '@rocket.chat/core-services'; import { Users } from '@rocket.chat/models'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; import { settings } from '../../../settings/server'; import { validateName } from './validateName'; diff --git a/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts b/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts index 3c7eb3950e96..64202ab39dab 100644 --- a/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts +++ b/apps/meteor/app/lib/server/functions/cleanRoomHistory.ts @@ -1,10 +1,10 @@ -import type { IRoom } from '@rocket.chat/core-typings'; import { api } from '@rocket.chat/core-services'; -import { Messages, Rooms, Subscriptions } from '@rocket.chat/models'; +import type { IRoom } from '@rocket.chat/core-typings'; +import { Messages, Rooms, Subscriptions, ReadReceipts, Users } from '@rocket.chat/models'; -import { deleteRoom } from './deleteRoom'; -import { FileUpload } from '../../../file-upload/server'; import { i18n } from '../../../../server/lib/i18n'; +import { FileUpload } from '../../../file-upload/server'; +import { deleteRoom } from './deleteRoom'; export async function cleanRoomHistory({ rid = '', @@ -86,6 +86,9 @@ export async function cleanRoomHistory({ } } + const selectedMessageIds = limit + ? await Messages.findByIdPinnedTimestampLimitAndUsers(rid, excludePinned, ignoreDiscussion, ts, limit, fromUsers, ignoreThreads) + : undefined; const count = await Messages.removeByIdPinnedTimestampLimitAndUsers( rid, excludePinned, @@ -94,7 +97,18 @@ export async function cleanRoomHistory({ limit, fromUsers, ignoreThreads, + selectedMessageIds, ); + + if (!limit) { + const uids = await Users.findByUsernames(fromUsers, { projection: { _id: 1 } }) + .map((user) => user._id) + .toArray(); + await ReadReceipts.removeByIdPinnedTimestampLimitAndUsers(rid, excludePinned, ignoreDiscussion, ts, uids, ignoreThreads); + } else if (selectedMessageIds) { + await ReadReceipts.removeByMessageIds(selectedMessageIds); + } + if (count) { const lastMessage = await Messages.getLastVisibleMessageSentWithNoTypeByRoomId(rid); await Rooms.resetLastMessageById(rid, lastMessage); diff --git a/apps/meteor/app/lib/server/functions/closeOmnichannelConversations.ts b/apps/meteor/app/lib/server/functions/closeOmnichannelConversations.ts index 871c6af198c5..fc464f762a98 100644 --- a/apps/meteor/app/lib/server/functions/closeOmnichannelConversations.ts +++ b/apps/meteor/app/lib/server/functions/closeOmnichannelConversations.ts @@ -1,10 +1,10 @@ import type { IUser } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; -import { settings } from '../../../settings/server'; -import { Livechat } from '../../../livechat/server/lib/LivechatTyped'; -import { i18n } from '../../../../server/lib/i18n'; import { callbacks } from '../../../../lib/callbacks'; +import { i18n } from '../../../../server/lib/i18n'; +import { Livechat } from '../../../livechat/server/lib/LivechatTyped'; +import { settings } from '../../../settings/server'; type SubscribedRooms = { rid: string; diff --git a/apps/meteor/app/lib/server/functions/createDirectRoom.ts b/apps/meteor/app/lib/server/functions/createDirectRoom.ts index c9a6ce7a35c3..92db702dd4ec 100644 --- a/apps/meteor/app/lib/server/functions/createDirectRoom.ts +++ b/apps/meteor/app/lib/server/functions/createDirectRoom.ts @@ -10,7 +10,7 @@ import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; import { settings } from '../../../settings/server'; -import { getDefaultSubscriptionPref } from '../../../utils/server'; +import { getDefaultSubscriptionPref } from '../../../utils/lib/getDefaultSubscriptionPref'; const generateSubscription = ( fname: string, diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 6d0048659501..192139f96b7c 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -1,14 +1,15 @@ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; -import { Meteor } from 'meteor/meteor'; -import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typings'; import { Message, Team } from '@rocket.chat/core-services'; import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services'; +import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typings'; import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { Apps } from '../../../../ee/server/apps'; -import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; +import { Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; -import { getValidRoomName } from '../../../utils/server'; +import { beforeCreateRoomCallback } from '../../../../lib/callbacks/beforeCreateRoomCallback'; +import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; +import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; import { createDirectRoom } from './createDirectRoom'; const isValidName = (name: unknown): name is string => { @@ -18,7 +19,6 @@ const isValidName = (name: unknown): name is string => { const onlyUsernames = (members: unknown): members is string[] => Array.isArray(members) && members.every((member) => typeof member === 'string'); -// eslint-disable-next-line complexity export const createRoom = async ( type: T, name: T extends 'd' ? undefined : string, @@ -34,7 +34,16 @@ export const createRoom = async ( } > => { const { teamId, ...extraData } = roomExtraData || ({} as IRoom); - await callbacks.run('beforeCreateRoom', { type, name, owner: ownerUsername, members, readOnly, extraData, options }); + await beforeCreateRoomCallback.run({ + type, + // name, + // owner: ownerUsername, + // members, + // readOnly, + extraData, + + // options, + }); if (type === 'd') { return createDirectRoom(members as IUser[], extraData, { ...options, creator: options?.creator || ownerUsername }); } diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index 2c08cac6d024..6fddbd719fa9 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import type { AtLeast, IMessage, IUser } from '@rocket.chat/core-typings'; -import { Messages, Rooms, Uploads, Users } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; +import type { AtLeast, IMessage, IUser } from '@rocket.chat/core-typings'; +import { Messages, Rooms, Uploads, Users, ReadReceipts } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { FileUpload } from '../../../file-upload/server'; -import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; import { Apps } from '../../../../ee/server/apps'; +import { callbacks } from '../../../../lib/callbacks'; import { canDeleteMessageAsync } from '../../../authorization/server/functions/canDeleteMessage'; +import { FileUpload } from '../../../file-upload/server'; +import { settings } from '../../../settings/server'; export const deleteMessageValidatingPermission = async (message: AtLeast, userId: IUser['_id']): Promise => { if (!message?._id) { @@ -62,6 +62,7 @@ export async function deleteMessage(message: IMessage, user: IUser): Promise { const user = await Users.findOneById(userId, { @@ -56,8 +57,9 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele } await Messages.removeByUserId(userId); + await ReadReceipts.removeByUserId(userId); - await ModerationReports.hideReportsByUserId( + await ModerationReports.hideMessageReportsByUserId( userId, deletedBy || userId, deletedBy === userId ? 'user deleted own account' : 'user account deleted', diff --git a/apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts b/apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts index aa6469eac7ef..a54704cf8862 100644 --- a/apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts +++ b/apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts @@ -1,9 +1,8 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { check } from 'meteor/check'; -import Gravatar from 'gravatar'; -import { ServiceConfiguration } from 'meteor/service-configuration'; import type { IUser } from '@rocket.chat/core-typings'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import Gravatar from 'gravatar'; +import { check } from 'meteor/check'; +import { ServiceConfiguration } from 'meteor/service-configuration'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/lib/server/functions/getFullUserData.ts b/apps/meteor/app/lib/server/functions/getFullUserData.ts index dd6696a18ce4..0703b24d9210 100644 --- a/apps/meteor/app/lib/server/functions/getFullUserData.ts +++ b/apps/meteor/app/lib/server/functions/getFullUserData.ts @@ -1,9 +1,9 @@ import type { IUser } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import { Users } from '@rocket.chat/models'; -import { Logger } from '../../../logger/server'; -import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { settings } from '../../../settings/server'; const logger = new Logger('getFullUserData'); @@ -21,6 +21,7 @@ const defaultFields = { avatarETag: 1, extension: 1, federated: 1, + statusLivechat: 1, } as const; const fullFields = { diff --git a/apps/meteor/app/lib/server/functions/getRoomByNameOrIdWithOptionToJoin.ts b/apps/meteor/app/lib/server/functions/getRoomByNameOrIdWithOptionToJoin.ts index d64d2da28ab8..113f8f59a557 100644 --- a/apps/meteor/app/lib/server/functions/getRoomByNameOrIdWithOptionToJoin.ts +++ b/apps/meteor/app/lib/server/functions/getRoomByNameOrIdWithOptionToJoin.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import type { IRoom, IUser, RoomType } from '@rocket.chat/core-typings'; import { Rooms, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { isObject } from '../../../../lib/utils/isObject'; import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; diff --git a/apps/meteor/app/lib/server/functions/getUserCreatedByApp.ts b/apps/meteor/app/lib/server/functions/getUserCreatedByApp.ts index 7073f9ccd541..f697e1fd66be 100644 --- a/apps/meteor/app/lib/server/functions/getUserCreatedByApp.ts +++ b/apps/meteor/app/lib/server/functions/getUserCreatedByApp.ts @@ -1,7 +1,7 @@ -import type { FindOptions } from 'mongodb'; +import type { UserType } from '@rocket.chat/apps-engine/definition/users'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import type { UserType } from '@rocket.chat/apps-engine/definition/users'; +import type { FindOptions } from 'mongodb'; export async function getUserCreatedByApp( appId: string, diff --git a/apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts b/apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts index 7249d1e1816c..9fba9e9246b2 100644 --- a/apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts +++ b/apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts @@ -1,6 +1,6 @@ -import limax from 'limax'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import limax from 'limax'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/lib/server/functions/index.ts b/apps/meteor/app/lib/server/functions/index.ts deleted file mode 100644 index 15092acbfb71..000000000000 --- a/apps/meteor/app/lib/server/functions/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -export { addUserToDefaultChannels } from './addUserToDefaultChannels'; -export { addUserToRoom } from './addUserToRoom'; -export { attachMessage } from './attachMessage'; -export { checkEmailAvailability } from './checkEmailAvailability'; -export { createRoom } from './createRoom'; -export { createDirectRoom } from './createDirectRoom'; -export { deleteMessage } from './deleteMessage'; -export { deleteRoom } from './deleteRoom'; -export { deleteUser } from './deleteUser'; -export { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms'; -export { getUserCreatedByApp } from './getUserCreatedByApp'; -export { generateUsernameSuggestion } from './getUsernameSuggestion'; -export { insertMessage } from './insertMessage'; -export { isTheLastMessage } from './isTheLastMessage'; -export { removeUserFromRoom } from './removeUserFromRoom'; -export { relinquishRoomOwnerships } from './relinquishRoomOwnerships'; -export { saveCustomFields } from './saveCustomFields'; -export { saveCustomFieldsWithoutValidation } from './saveCustomFieldsWithoutValidation'; -export { saveUser } from './saveUser'; -export { saveUserIdentity } from './saveUserIdentity'; -export { sendMessage } from './sendMessage'; -export { setEmail } from './setEmail'; -export { setRealName, _setRealName } from './setRealName'; -export { getStatusText } from './getStatusText'; -export { setUserAvatar } from './setUserAvatar'; -export { _setUsername, setUsername, setUsernameWithValidation } from './setUsername'; -export { unarchiveRoom } from './unarchiveRoom'; -export { updateMessage } from './updateMessage'; -export { validateCustomFields } from './validateCustomFields'; -export { validateName } from './validateName'; diff --git a/apps/meteor/app/lib/server/functions/insertMessage.ts b/apps/meteor/app/lib/server/functions/insertMessage.ts index f09d086384ba..ab20be0dfe67 100644 --- a/apps/meteor/app/lib/server/functions/insertMessage.ts +++ b/apps/meteor/app/lib/server/functions/insertMessage.ts @@ -1,8 +1,8 @@ -import { Messages, Rooms } from '@rocket.chat/models'; import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import { Messages, Rooms } from '@rocket.chat/models'; -import { validateMessage, prepareMessageObject } from './sendMessage'; import { parseUrlsInMessage } from './parseUrlsInMessage'; +import { validateMessage, prepareMessageObject } from './sendMessage'; export const insertMessage = async function ( user: Pick, diff --git a/apps/meteor/app/lib/server/functions/notifications/email.js b/apps/meteor/app/lib/server/functions/notifications/email.js index fbf7218380a7..b0953293ac73 100644 --- a/apps/meteor/app/lib/server/functions/notifications/email.js +++ b/apps/meteor/app/lib/server/functions/notifications/email.js @@ -1,19 +1,19 @@ -import { Meteor } from 'meteor/meteor'; import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; -import * as Mailer from '../../../../mailer/server/api'; -import { settings } from '../../../../settings/server'; -import { metrics } from '../../../../metrics/server'; import { callbacks } from '../../../../../lib/callbacks'; -import { getURL } from '../../../../utils/server'; -import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; import { ltrim } from '../../../../../lib/utils/stringUtils'; import { i18n } from '../../../../../server/lib/i18n'; +import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; +import * as Mailer from '../../../../mailer/server/api'; +import { metrics } from '../../../../metrics/server'; +import { settings } from '../../../../settings/server'; +import { getURL } from '../../../../utils/server/getURL'; let advice = ''; let goToMessage = ''; Meteor.startup(() => { - settings.watch('email_style', function () { + settings.watch('email_style', () => { goToMessage = Mailer.inlinecss('

{Offline_Link_Message}

'); }); Mailer.getTemplate('Email_Footer_Direct_Reply', (value) => { diff --git a/apps/meteor/app/lib/server/functions/notifications/index.ts b/apps/meteor/app/lib/server/functions/notifications/index.ts index efc74c47d4dc..934014b794a1 100644 --- a/apps/meteor/app/lib/server/functions/notifications/index.ts +++ b/apps/meteor/app/lib/server/functions/notifications/index.ts @@ -1,11 +1,11 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { isFileAttachment, isFileImageAttachment } from '@rocket.chat/core-typings'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; import { callbacks } from '../../../../../lib/callbacks'; +import { i18n } from '../../../../../server/lib/i18n'; import { settings } from '../../../../settings/server'; import { joinRoomMethod } from '../../methods/joinRoom'; -import { i18n } from '../../../../../server/lib/i18n'; /** * This function returns a string ready to be shown in the notification diff --git a/apps/meteor/app/lib/server/functions/notifications/mobile.js b/apps/meteor/app/lib/server/functions/notifications/mobile.js index b98f37042e98..b4139bea6f12 100644 --- a/apps/meteor/app/lib/server/functions/notifications/mobile.js +++ b/apps/meteor/app/lib/server/functions/notifications/mobile.js @@ -1,8 +1,8 @@ import { Subscriptions } from '@rocket.chat/models'; -import { settings } from '../../../../settings/server'; -import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; import { i18n } from '../../../../../server/lib/i18n'; +import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; +import { settings } from '../../../../settings/server'; const CATEGORY_MESSAGE = 'MESSAGE'; const CATEGORY_MESSAGE_NOREPLY = 'MESSAGE_NOREPLY'; diff --git a/apps/meteor/app/lib/server/functions/parseUrlsInMessage.ts b/apps/meteor/app/lib/server/functions/parseUrlsInMessage.ts index 14cc98303b55..2a63d024bdd9 100644 --- a/apps/meteor/app/lib/server/functions/parseUrlsInMessage.ts +++ b/apps/meteor/app/lib/server/functions/parseUrlsInMessage.ts @@ -1,9 +1,10 @@ -import type { IMessage } from '@rocket.chat/core-typings'; +import type { IMessage, AtLeast } from '@rocket.chat/core-typings'; -import { Markdown } from '../../../markdown/server'; import { getMessageUrlRegex } from '../../../../lib/getMessageUrlRegex'; +import { Markdown } from '../../../markdown/server'; +import { settings } from '../../../settings/server'; -export const parseUrlsInMessage = (message: IMessage & { parseUrls?: boolean }): IMessage => { +export const parseUrlsInMessage = (message: AtLeast & { parseUrls?: boolean }, previewUrls?: string[]) => { if (message.parseUrls === false) { return message; } @@ -13,13 +14,15 @@ export const parseUrlsInMessage = (message: IMessage & { parseUrls?: boolean }): const urls = message.html?.match(getMessageUrlRegex()) || []; if (urls) { - message.urls = [...new Set(urls)].map((url) => ({ url, meta: {} })); + message.urls = [...new Set(urls)].map((url) => ({ + url, + meta: {}, + ...(previewUrls && !previewUrls.includes(url) && !url.includes(settings.get('Site_Url')) && { ignoreParse: true }), + })); } message = Markdown.mountTokensBack(message, false); message.msg = message.html || message.msg; delete message.html; delete message.tokens; - - return message; }; diff --git a/apps/meteor/app/lib/server/functions/processWebhookMessage.js b/apps/meteor/app/lib/server/functions/processWebhookMessage.js index e92b61cb691f..1e5c34b287bb 100644 --- a/apps/meteor/app/lib/server/functions/processWebhookMessage.js +++ b/apps/meteor/app/lib/server/functions/processWebhookMessage.js @@ -1,11 +1,11 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; +import { trim } from '../../../../lib/utils/stringUtils'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { validateRoomMessagePermissionsAsync } from '../../../authorization/server/functions/canSendMessage'; import { getRoomByNameOrIdWithOptionToJoin } from './getRoomByNameOrIdWithOptionToJoin'; import { sendMessage } from './sendMessage'; -import { validateRoomMessagePermissionsAsync } from '../../../authorization/server/functions/canSendMessage'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { trim } from '../../../../lib/utils/stringUtils'; export const processWebhookMessage = async function (messageObj, user, defaultValues = { channel: '', alias: '', avatar: '', emoji: '' }) { const sentData = []; diff --git a/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts b/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts index da566d859393..75b232462077 100644 --- a/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts +++ b/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts @@ -1,4 +1,4 @@ -import { Messages, Roles, Rooms, Subscriptions } from '@rocket.chat/models'; +import { Messages, Roles, Rooms, Subscriptions, ReadReceipts } from '@rocket.chat/models'; import { FileUpload } from '../../../file-upload/server'; import type { SubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner'; @@ -7,7 +7,12 @@ const bulkRoomCleanUp = async (rids: string[]): Promise => { // no bulk deletion for files await Promise.all(rids.map((rid) => FileUpload.removeFilesByRoomId(rid))); - return Promise.all([Subscriptions.removeByRoomIds(rids), Messages.removeByRoomIds(rids), Rooms.removeByIds(rids)]); + return Promise.all([ + Subscriptions.removeByRoomIds(rids), + Messages.removeByRoomIds(rids), + ReadReceipts.removeByRoomIds(rids), + Rooms.removeByIds(rids), + ]); }; export const relinquishRoomOwnerships = async function ( diff --git a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts index bca939bc520b..b2925bfe37f2 100644 --- a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts +++ b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts @@ -1,12 +1,12 @@ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; -import { Meteor } from 'meteor/meteor'; -import type { IUser } from '@rocket.chat/core-typings'; import { Message, Team } from '@rocket.chat/core-services'; +import type { IUser } from '@rocket.chat/core-typings'; import { Subscriptions, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { AppEvents, Apps } from '../../../../ee/server/apps'; -import { callbacks } from '../../../../lib/callbacks'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; +import { afterLeaveRoomCallback } from '../../../../lib/callbacks/afterLeaveRoomCallback'; +import { beforeLeaveRoomCallback } from '../../../../lib/callbacks/beforeLeaveRoomCallback'; export const removeUserFromRoom = async function ( rid: string, @@ -29,7 +29,7 @@ export const removeUserFromRoom = async function ( throw error; } - await callbacks.run('beforeLeaveRoom', user, room); + await beforeLeaveRoomCallback.run(user, room); const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id, { projection: { _id: 1 }, @@ -65,7 +65,7 @@ export const removeUserFromRoom = async function ( } // TODO: CACHE: maybe a queue? - await callbacks.run('afterLeaveRoom', user, room); + await afterLeaveRoomCallback.run(user, room); await Apps.triggerEvent(AppEvents.IPostRoomUserLeave, room, user); }; diff --git a/apps/meteor/app/lib/server/functions/saveCustomFields.ts b/apps/meteor/app/lib/server/functions/saveCustomFields.ts index 79e8e81dbe7b..0657ca4bbf2b 100644 --- a/apps/meteor/app/lib/server/functions/saveCustomFields.ts +++ b/apps/meteor/app/lib/server/functions/saveCustomFields.ts @@ -1,6 +1,7 @@ -import { settings } from '../../../settings/server'; -import { validateCustomFields, saveCustomFieldsWithoutValidation } from '.'; import { trim } from '../../../../lib/utils/stringUtils'; +import { settings } from '../../../settings/server'; +import { saveCustomFieldsWithoutValidation } from './saveCustomFieldsWithoutValidation'; +import { validateCustomFields } from './validateCustomFields'; export const saveCustomFields = async function (userId: string, formData: Record): Promise { if (trim(settings.get('Accounts_CustomFields')) !== '') { diff --git a/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts b/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts index 8372d349b7ee..4a0ac005e55c 100644 --- a/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts +++ b/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { Subscriptions, Users } from '@rocket.chat/models'; import type { IUser, DeepWritable } from '@rocket.chat/core-typings'; +import { Subscriptions, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import type { UpdateFilter } from 'mongodb'; -import { settings } from '../../../settings/server'; import { trim } from '../../../../lib/utils/stringUtils'; +import { settings } from '../../../settings/server'; export const saveCustomFieldsWithoutValidation = async function (userId: string, formData: Record): Promise { if (trim(settings.get('Accounts_CustomFields')) !== '') { diff --git a/apps/meteor/app/lib/server/functions/saveUser.js b/apps/meteor/app/lib/server/functions/saveUser.js index 650a9b05bc31..f912626c833e 100644 --- a/apps/meteor/app/lib/server/functions/saveUser.js +++ b/apps/meteor/app/lib/server/functions/saveUser.js @@ -1,25 +1,27 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; -import _ from 'underscore'; -import Gravatar from 'gravatar'; import { isUserFederated } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import Gravatar from 'gravatar'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; -import * as Mailer from '../../../mailer/server/api'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; +import { callbacks } from '../../../../lib/callbacks'; +import { trim } from '../../../../lib/utils/stringUtils'; +import { getNewUserRoles } from '../../../../server/services/user/lib/getNewUserRoles'; import { getRoles } from '../../../authorization/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; -import { passwordPolicy } from '../lib/passwordPolicy'; +import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser'; import { validateEmailDomain } from '../lib'; -import { getNewUserRoles } from '../../../../server/services/user/lib/getNewUserRoles'; +import { passwordPolicy } from '../lib/passwordPolicy'; +import { checkEmailAvailability } from './checkEmailAvailability'; +import { checkUsernameAvailability } from './checkUsernameAvailability'; import { saveUserIdentity } from './saveUserIdentity'; -import { checkEmailAvailability, setUserAvatar, setEmail } from '.'; +import { setEmail } from './setEmail'; import { setStatusText } from './setStatusText'; -import { checkUsernameAvailability } from './checkUsernameAvailability'; -import { callbacks } from '../../../../lib/callbacks'; -import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; -import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser'; -import { trim } from '../../../../lib/utils/stringUtils'; +import { setUserAvatar } from './setUserAvatar'; const MAX_BIO_LENGTH = 260; const MAX_NICKNAME_LENGTH = 120; @@ -237,8 +239,8 @@ export async function validateUserEditing(userId, userData) { const handleBio = (updateUser, bio) => { if (bio && bio.trim()) { - if (typeof bio !== 'string' || bio.length > MAX_BIO_LENGTH) { - throw new Meteor.Error('error-invalid-field', 'bio', { + if (bio.length > MAX_BIO_LENGTH) { + throw new Meteor.Error('error-bio-size-exceeded', `Bio size exceeds ${MAX_BIO_LENGTH} characters`, { method: 'saveUserProfile', }); } @@ -252,8 +254,8 @@ const handleBio = (updateUser, bio) => { const handleNickname = (updateUser, nickname) => { if (nickname && nickname.trim()) { - if (typeof nickname !== 'string' || nickname.length > MAX_NICKNAME_LENGTH) { - throw new Meteor.Error('error-invalid-field', 'nickname', { + if (nickname.length > MAX_NICKNAME_LENGTH) { + throw new Meteor.Error('error-nickname-size-exceeded', `Nickname size exceeds ${MAX_NICKNAME_LENGTH} characters`, { method: 'saveUserProfile', }); } diff --git a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts index 37ec3a890bd3..2eb360e150c6 100644 --- a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts +++ b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts @@ -1,8 +1,8 @@ import { Messages, VideoConference, LivechatDepartmentAgents, Rooms, Subscriptions, Users } from '@rocket.chat/models'; -import { _setUsername } from './setUsername'; -import { _setRealName } from './setRealName'; import { FileUpload } from '../../../file-upload/server'; +import { _setRealName } from './setRealName'; +import { _setUsername } from './setUsername'; import { updateGroupDMsName } from './updateGroupDMsName'; import { validateName } from './validateName'; diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.js index 75c6e06120b6..a1399b5b19e9 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.js @@ -1,15 +1,15 @@ -import { Match, check } from 'meteor/check'; import { Messages } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; -import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; import { Apps } from '../../../../ee/server/apps'; +import { callbacks } from '../../../../lib/callbacks'; +import { isRelativeURL } from '../../../../lib/utils/isRelativeURL'; import { isURL } from '../../../../lib/utils/isURL'; -import { FileUpload } from '../../../file-upload/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { parseUrlsInMessage } from './parseUrlsInMessage'; -import { isRelativeURL } from '../../../../lib/utils/isRelativeURL'; +import { FileUpload } from '../../../file-upload/server'; import notifications from '../../../notifications/server/lib/Notifications'; +import { settings } from '../../../settings/server'; +import { parseUrlsInMessage } from './parseUrlsInMessage'; /** * IMPORTANT @@ -203,7 +203,16 @@ function cleanupMessageObject(message) { ['customClass'].forEach((field) => delete message[field]); } -export const sendMessage = async function (user, message, room, upsert = false) { +/** + * Validates and sends the message object. + * @param {IUser} user + * @param {AtLeast} message + * @param {IRoom} room + * @param {boolean} [upsert=false] + * @param {string[]} [previewUrls] + * @returns {Promise} + */ +export const sendMessage = async function (user, message, room, upsert = false, previewUrls = undefined) { if (!user || !message || !room._id) { return false; } @@ -236,7 +245,7 @@ export const sendMessage = async function (user, message, room, upsert = false) cleanupMessageObject(message); - parseUrlsInMessage(message); + parseUrlsInMessage(message, previewUrls); message = await callbacks.run('beforeSaveMessage', message, room); if (message) { diff --git a/apps/meteor/app/lib/server/functions/setEmail.ts b/apps/meteor/app/lib/server/functions/setEmail.ts index a23ab5ff8f8b..1ab42daf99e5 100644 --- a/apps/meteor/app/lib/server/functions/setEmail.ts +++ b/apps/meteor/app/lib/server/functions/setEmail.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { escapeHTML } from '@rocket.chat/string-helpers'; import { Users } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { RateLimiter, validateEmailDomain } from '../lib'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; -import { checkEmailAvailability } from '.'; +import { RateLimiter, validateEmailDomain } from '../lib'; +import { checkEmailAvailability } from './checkEmailAvailability'; let html = ''; Meteor.startup(() => { diff --git a/apps/meteor/app/lib/server/functions/setRealName.ts b/apps/meteor/app/lib/server/functions/setRealName.ts index 90089385209c..4ad08010fc81 100644 --- a/apps/meteor/app/lib/server/functions/setRealName.ts +++ b/apps/meteor/app/lib/server/functions/setRealName.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import type { IUser } from '@rocket.chat/core-typings'; import { api } from '@rocket.chat/core-services'; +import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { settings } from '../../../settings/server'; import { RateLimiter } from '../lib'; export const _setRealName = async function (userId: string, name: string, fullUser?: IUser): Promise { diff --git a/apps/meteor/app/lib/server/functions/setRoomAvatar.ts b/apps/meteor/app/lib/server/functions/setRoomAvatar.ts index 1da89604de0e..42fc615f09db 100644 --- a/apps/meteor/app/lib/server/functions/setRoomAvatar.ts +++ b/apps/meteor/app/lib/server/functions/setRoomAvatar.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; +import { api, Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { isRegisterUser } from '@rocket.chat/core-typings'; import { Avatars, Rooms } from '@rocket.chat/models'; -import { api, Message } from '@rocket.chat/core-services'; +import { Meteor } from 'meteor/meteor'; -import { RocketChatFile } from '../../../file/server'; import { FileUpload } from '../../../file-upload/server'; +import { RocketChatFile } from '../../../file/server'; export const setRoomAvatar = async function (rid: string, dataURI: string, user: IUser): Promise { if (!isRegisterUser(user)) { @@ -43,7 +43,7 @@ export const setRoomAvatar = async function (rid: string, dataURI: string, user: const result = await fileStore.insert(file, buffer); - setTimeout(async function () { + setTimeout(async () => { result.etag && (await Rooms.setAvatarData(rid, 'upload', result.etag)); await Message.saveSystemMessage('room_changed_avatar', rid, '', user); void api.broadcast('room.avatarUpdate', { _id: rid, avatarETag: result.etag }); diff --git a/apps/meteor/app/lib/server/functions/setStatusText.ts b/apps/meteor/app/lib/server/functions/setStatusText.ts index 98ff7ab0d5bc..d55e5a8745d3 100644 --- a/apps/meteor/app/lib/server/functions/setStatusText.ts +++ b/apps/meteor/app/lib/server/functions/setStatusText.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import { api } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import { api } from '@rocket.chat/core-services'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RateLimiter } from '../lib'; diff --git a/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts b/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts index 401c2db14767..2d99fb427a46 100644 --- a/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts +++ b/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts @@ -1,17 +1,17 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { Accounts } from 'meteor/accounts-base'; import type { IUser, IUserEmail } from '@rocket.chat/core-typings'; import { isUserFederated, isDirectMessageRoom } from '@rocket.chat/core-typings'; import { Rooms, Users, Subscriptions } from '@rocket.chat/models'; +import { Accounts } from 'meteor/accounts-base'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { callbacks } from '../../../../lib/callbacks'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; -import { relinquishRoomOwnerships } from './relinquishRoomOwnerships'; import { closeOmnichannelConversations } from './closeOmnichannelConversations'; import { shouldRemoveOrChangeOwner, getSubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner'; import { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms'; +import { relinquishRoomOwnerships } from './relinquishRoomOwnerships'; async function reactivateDirectConversations(userId: string) { // since both users can be deactivated at the same time, we should just reactivate rooms if both users are active diff --git a/apps/meteor/app/lib/server/functions/setUserAvatar.ts b/apps/meteor/app/lib/server/functions/setUserAvatar.ts index ba9185d63f18..b46f0ff8cd50 100644 --- a/apps/meteor/app/lib/server/functions/setUserAvatar.ts +++ b/apps/meteor/app/lib/server/functions/setUserAvatar.ts @@ -1,15 +1,15 @@ -import { Meteor } from 'meteor/meteor'; -import type { IUser } from '@rocket.chat/core-typings'; import { api } from '@rocket.chat/core-services'; +import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import type { Response } from '@rocket.chat/server-fetch'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { Meteor } from 'meteor/meteor'; -import { RocketChatFile } from '../../../file/server'; -import { FileUpload } from '../../../file-upload/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; -import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { FileUpload } from '../../../file-upload/server'; +import { RocketChatFile } from '../../../file/server'; +import { settings } from '../../../settings/server'; export const setAvatarFromServiceWithValidation = async ( userId: string, @@ -165,7 +165,7 @@ export async function setUserAvatar( const avatarETag = etag || result?.etag || ''; - setTimeout(async function () { + setTimeout(async () => { if (service) { await Users.setAvatarData(user._id, service, avatarETag); void api.broadcast('user.avatarUpdate', { diff --git a/apps/meteor/app/lib/server/functions/setUsername.ts b/apps/meteor/app/lib/server/functions/setUsername.ts index 396f3a064cc2..319202cefea4 100644 --- a/apps/meteor/app/lib/server/functions/setUsername.ts +++ b/apps/meteor/app/lib/server/functions/setUsername.ts @@ -1,20 +1,21 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; +import { api } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { Invites, Users } from '@rocket.chat/models'; -import { api } from '@rocket.chat/core-services'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { settings } from '../../../settings/server'; +import { callbacks } from '../../../../lib/callbacks'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { settings } from '../../../settings/server'; import { RateLimiter } from '../lib'; import { addUserToRoom } from './addUserToRoom'; -import { saveUserIdentity, setUserAvatar } from '.'; import { checkUsernameAvailability } from './checkUsernameAvailability'; import { getAvatarSuggestionForUser } from './getAvatarSuggestionForUser'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { callbacks } from '../../../../lib/callbacks'; import { joinDefaultChannels } from './joinDefaultChannels'; +import { saveUserIdentity } from './saveUserIdentity'; +import { setUserAvatar } from './setUserAvatar'; export const setUsernameWithValidation = async (userId: string, username: string, joinDefaultChannelsSilenced?: boolean): Promise => { if (!username) { diff --git a/apps/meteor/app/lib/server/functions/unarchiveRoom.ts b/apps/meteor/app/lib/server/functions/unarchiveRoom.ts index a2ca7b3eba34..f068827d5c7c 100644 --- a/apps/meteor/app/lib/server/functions/unarchiveRoom.ts +++ b/apps/meteor/app/lib/server/functions/unarchiveRoom.ts @@ -1,6 +1,6 @@ -import { Rooms, Subscriptions } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; import type { IMessage } from '@rocket.chat/core-typings'; +import { Rooms, Subscriptions } from '@rocket.chat/models'; export const unarchiveRoom = async function (rid: string, user: IMessage['u']): Promise { await Rooms.unarchiveById(rid); diff --git a/apps/meteor/app/lib/server/functions/updateMessage.ts b/apps/meteor/app/lib/server/functions/updateMessage.ts index 83fdd4001214..05c17906374e 100644 --- a/apps/meteor/app/lib/server/functions/updateMessage.ts +++ b/apps/meteor/app/lib/server/functions/updateMessage.ts @@ -1,13 +1,18 @@ -import type { IEditedMessage, IMessage, IUser } from '@rocket.chat/core-typings'; -import { Meteor } from 'meteor/meteor'; +import type { IEditedMessage, IMessage, IUser, AtLeast } from '@rocket.chat/core-typings'; import { Messages, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; import { Apps } from '../../../../ee/server/apps'; +import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; import { parseUrlsInMessage } from './parseUrlsInMessage'; -export const updateMessage = async function (message: IMessage, user: IUser, originalMsg?: IMessage): Promise { +export const updateMessage = async function ( + message: AtLeast, + user: IUser, + originalMsg?: IMessage, + previewUrls?: string[], +): Promise { const originalMessage = originalMsg || (await Messages.findOneById(message._id)); // For the Rocket.Chat Apps :) @@ -33,7 +38,7 @@ export const updateMessage = async function (message: IMessage, user: IUser, ori await Messages.cloneAndSaveAsHistoryById(message._id, user as Required>); } - Object.assign>(message, { + Object.assign, Omit>(message, { editedAt: new Date(), editedBy: { _id: user._id, @@ -41,7 +46,7 @@ export const updateMessage = async function (message: IMessage, user: IUser, ori }, }); - parseUrlsInMessage(message); + parseUrlsInMessage(message, previewUrls); message = await callbacks.run('beforeSaveMessage', message); @@ -74,7 +79,7 @@ export const updateMessage = async function (message: IMessage, user: IUser, ori void Apps.getBridges()?.getListenerBridge().messageEvent('IPostMessageUpdated', message); } - setImmediate(async function () { + setImmediate(async () => { const msg = await Messages.findOneById(_id); if (msg) { await callbacks.run('afterSaveMessage', msg, room, user._id); diff --git a/apps/meteor/app/lib/server/index.ts b/apps/meteor/app/lib/server/index.ts index 17ef46312d2d..597d03752dcc 100644 --- a/apps/meteor/app/lib/server/index.ts +++ b/apps/meteor/app/lib/server/index.ts @@ -53,4 +53,3 @@ import './methods/updateMessage'; import './methods/saveCustomFields'; export * from './lib'; -export * from './functions'; diff --git a/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js b/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js index dd099452e732..99dc0d6fabf5 100644 --- a/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js +++ b/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { Random } from '@rocket.chat/random'; import generator from 'generate-password'; +import { Meteor } from 'meteor/meteor'; class PasswordPolicy { constructor({ @@ -43,15 +43,16 @@ class PasswordPolicy { return this._forbidRepeatingCharactersCount; } - error(error, message) { + error(error, message, reasons) { if (this.throwError) { - throw new Meteor.Error(error, message); + throw new Meteor.Error(error, message, reasons); } return false; } validate(password) { + const reasons = []; if (typeof password !== 'string' || !password.trim().length) { return this.error('error-password-policy-not-met', "The password provided does not meet the server's password policy."); } @@ -61,46 +62,56 @@ class PasswordPolicy { } if (this.minLength >= 1 && password.length < this.minLength) { - return this.error('error-password-policy-not-met-minLength', 'The password does not meet the minimum length password policy.'); + reasons.push({ + error: 'error-password-policy-not-met-minLength', + message: 'The password does not meet the minimum length password policy.', + }); } if (this.maxLength >= 1 && password.length > this.maxLength) { - return this.error('error-password-policy-not-met-maxLength', 'The password does not meet the maximum length password policy.'); + reasons.push({ + error: 'error-password-policy-not-met-maxLength', + message: 'The password does not meet the maximum length password policy.', + }); } if (this.forbidRepeatingCharacters && this.regex.forbiddingRepeatingCharacters.test(password)) { - return this.error( - 'error-password-policy-not-met-repeatingCharacters', - 'The password contains repeating characters which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-repeatingCharacters', + message: 'The password contains repeating characters which is against the password policy.', + }); } if (this.mustContainAtLeastOneLowercase && !this.regex.mustContainAtLeastOneLowercase.test(password)) { - return this.error( - 'error-password-policy-not-met-oneLowercase', - 'The password does not contain at least one lowercase character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneLowercase', + message: 'The password does not contain at least one lowercase character which is against the password policy.', + }); } if (this.mustContainAtLeastOneUppercase && !this.regex.mustContainAtLeastOneUppercase.test(password)) { - return this.error( - 'error-password-policy-not-met-oneUppercase', - 'The password does not contain at least one uppercase character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneUppercase', + message: 'The password does not contain at least one uppercase character which is against the password policy.', + }); } if (this.mustContainAtLeastOneNumber && !this.regex.mustContainAtLeastOneNumber.test(password)) { - return this.error( - 'error-password-policy-not-met-oneNumber', - 'The password does not contain at least one numerical character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneNumber', + message: 'The password does not contain at least one numerical character which is against the password policy.', + }); } if (this.mustContainAtLeastOneSpecialCharacter && !this.regex.mustContainAtLeastOneSpecialCharacter.test(password)) { - return this.error( - 'error-password-policy-not-met-oneSpecial', - 'The password does not contain at least one special character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneSpecial', + message: 'The password does not contain at least one special character which is against the password policy.', + }); + } + + if (reasons.length) { + return this.error('error-password-policy-not-met', `The password provided does not meet the server's password policy.`, reasons); } return true; diff --git a/apps/meteor/app/lib/server/lib/RateLimiter.js b/apps/meteor/app/lib/server/lib/RateLimiter.js index d6c893f9567b..b8c069ed290e 100644 --- a/apps/meteor/app/lib/server/lib/RateLimiter.js +++ b/apps/meteor/app/lib/server/lib/RateLimiter.js @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { Meteor } from 'meteor/meteor'; import { RateLimiter } from 'meteor/rate-limit'; export const RateLimiterClass = new (class { @@ -8,7 +8,7 @@ export const RateLimiterClass = new (class { return fn; } const rateLimiter = new RateLimiter(); - Object.entries(matchers).forEach(function ([key, matcher]) { + Object.entries(matchers).forEach(([key, matcher]) => { matchers[key] = (...args) => Promise.await(matcher(...args)); }); @@ -16,7 +16,7 @@ export const RateLimiterClass = new (class { return function (...args) { const match = {}; - Object.keys(matchers).forEach(function (key) { + Object.keys(matchers).forEach((key) => { match[key] = args[key]; }); @@ -46,7 +46,7 @@ export const RateLimiterClass = new (class { type: 'method', name: methodName, }; - Object.entries(matchers).forEach(function ([key, matcher]) { + Object.entries(matchers).forEach(([key, matcher]) => { match[key] = (...args) => Promise.await(matcher(...args)); }); return DDPRateLimiter.addRule(match, numRequests, timeInterval); diff --git a/apps/meteor/app/lib/server/lib/bugsnag.ts b/apps/meteor/app/lib/server/lib/bugsnag.ts index a7b907064b83..25dbfe0fdc0e 100644 --- a/apps/meteor/app/lib/server/lib/bugsnag.ts +++ b/apps/meteor/app/lib/server/lib/bugsnag.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import Bugsnag from '@bugsnag/js'; +import { Logger } from '@rocket.chat/logger'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; -import { Info } from '../../../utils/server'; -import { Logger } from '../../../logger/server'; +import { Info } from '../../../utils/rocketchat.info'; const logger = new Logger('bugsnag'); diff --git a/apps/meteor/app/lib/server/lib/debug.js b/apps/meteor/app/lib/server/lib/debug.js index f8215c8b9054..aaa492e80337 100644 --- a/apps/meteor/app/lib/server/lib/debug.js +++ b/apps/meteor/app/lib/server/lib/debug.js @@ -1,12 +1,12 @@ +import { InstanceStatus } from '@rocket.chat/instance-status'; +import { Logger } from '@rocket.chat/logger'; import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; -import { InstanceStatus } from '@rocket.chat/instance-status'; import _ from 'underscore'; -import { settings } from '../../../settings/server'; -import { metrics } from '../../../metrics/server'; -import { Logger } from '../../../../server/lib/logger/Logger'; import { getMethodArgs } from '../../../../server/lib/logger/logPayloads'; +import { metrics } from '../../../metrics/server'; +import { settings } from '../../../settings/server'; const logger = new Logger('Meteor'); @@ -80,7 +80,7 @@ const wrapMethods = function (name, originalHandler, methodsMap) { const originalMeteorMethods = Meteor.methods; Meteor.methods = function (methodMap) { - _.each(methodMap, function (handler, name) { + _.each(methodMap, (handler, name) => { wrapMethods(name, handler, methodMap); }); originalMeteorMethods(methodMap); @@ -113,7 +113,7 @@ Meteor.publish = function (name, func) { }); }; -WebApp.rawConnectHandlers.use(function (req, res, next) { +WebApp.rawConnectHandlers.use((req, res, next) => { res.setHeader('X-Instance-ID', InstanceStatus.id()); return next(); }); diff --git a/apps/meteor/app/lib/server/lib/deprecationWarningLogger.ts b/apps/meteor/app/lib/server/lib/deprecationWarningLogger.ts index a13bb7b59bfc..6ca8e3edfbb9 100644 --- a/apps/meteor/app/lib/server/lib/deprecationWarningLogger.ts +++ b/apps/meteor/app/lib/server/lib/deprecationWarningLogger.ts @@ -1,7 +1,7 @@ +import { Logger } from '@rocket.chat/logger'; import type { Response } from 'meteor/rocketchat:restivus'; import semver from 'semver'; -import { Logger } from '../../../logger/server'; import { metrics } from '../../../metrics/server'; const deprecationLogger = new Logger('DeprecationWarning'); diff --git a/apps/meteor/app/lib/server/lib/getHiddenSystemMessages.ts b/apps/meteor/app/lib/server/lib/getHiddenSystemMessages.ts index d1949ea6c24d..74b31b63da6a 100644 --- a/apps/meteor/app/lib/server/lib/getHiddenSystemMessages.ts +++ b/apps/meteor/app/lib/server/lib/getHiddenSystemMessages.ts @@ -4,7 +4,7 @@ import { settings } from '../../../settings/server'; const hideMessagesOfTypeServer = new Set(); -settings.watch('Hide_System_Messages', function (values) { +settings.watch('Hide_System_Messages', (values) => { if (!values || !Array.isArray(values)) { return; } diff --git a/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js b/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js index 17cde1d48f4a..41a704d5afb6 100644 --- a/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js +++ b/apps/meteor/app/lib/server/lib/interceptDirectReplyEmails.js @@ -1,8 +1,8 @@ import POP3Lib from '@rocket.chat/poplib'; import { simpleParser } from 'mailparser'; -import { settings } from '../../../settings/server'; import { IMAPInterceptor } from '../../../../server/email/IMAPInterceptor'; +import { settings } from '../../../settings/server'; import { processDirectEmail } from './processDirectEmail'; export class DirectReplyIMAPInterceptor extends IMAPInterceptor { @@ -98,16 +98,16 @@ class POP3Intercepter { }); // invalid server state - this.pop3.on('invalid-state', function (cmd) { + this.pop3.on('invalid-state', (cmd) => { console.log(`Invalid state. You tried calling ${cmd}`); }); - this.pop3.on('error', function (cmd) { + this.pop3.on('error', (cmd) => { console.log(`error state. You tried calling ${cmd}`); }); // locked => command already running, not finished yet - this.pop3.on('locked', function (cmd) { + this.pop3.on('locked', (cmd) => { console.log(`Current command has not finished yet. You tried calling ${cmd}`); }); } diff --git a/apps/meteor/app/lib/server/lib/loginErrorMessageOverride.js b/apps/meteor/app/lib/server/lib/loginErrorMessageOverride.js index 4160470d660e..4e054b81b2cf 100644 --- a/apps/meteor/app/lib/server/lib/loginErrorMessageOverride.js +++ b/apps/meteor/app/lib/server/lib/loginErrorMessageOverride.js @@ -1,6 +1,6 @@ // Do not disclose if user exists when password is invalid -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; const { _runLoginHandlers } = Accounts; Accounts._runLoginHandlers = function (methodInvocation, options) { diff --git a/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js b/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js index 8ee81de0f44f..8c70ee3f9a4f 100644 --- a/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js +++ b/apps/meteor/app/lib/server/lib/notifyUsersOnMessage.js @@ -1,9 +1,9 @@ -import moment from 'moment'; -import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Subscriptions, Rooms } from '@rocket.chat/models'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; +import moment from 'moment'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; /** * Chechs if a messages contains a user highlight @@ -18,7 +18,7 @@ function messageContainsHighlight(message, highlights) { return false; } - return highlights.some(function (highlight) { + return highlights.some((highlight) => { const regexp = new RegExp(escapeRegExp(highlight), 'i'); return regexp.test(message.msg); }); diff --git a/apps/meteor/app/lib/server/lib/passwordPolicy.js b/apps/meteor/app/lib/server/lib/passwordPolicy.js index 450cb86116d7..57490d1712c1 100644 --- a/apps/meteor/app/lib/server/lib/passwordPolicy.js +++ b/apps/meteor/app/lib/server/lib/passwordPolicy.js @@ -1,5 +1,5 @@ -import PasswordPolicy from './PasswordPolicyClass'; import { settings } from '../../../settings/server'; +import PasswordPolicy from './PasswordPolicyClass'; export const passwordPolicy = new PasswordPolicy(); diff --git a/apps/meteor/app/lib/server/lib/processDirectEmail.ts b/apps/meteor/app/lib/server/lib/processDirectEmail.ts index 98b2e14c7721..e88b7d58468e 100644 --- a/apps/meteor/app/lib/server/lib/processDirectEmail.ts +++ b/apps/meteor/app/lib/server/lib/processDirectEmail.ts @@ -1,12 +1,12 @@ -import moment from 'moment'; -import type { ParsedMail } from 'mailparser'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import type { ParsedMail } from 'mailparser'; +import moment from 'moment'; -import { settings } from '../../../settings/server'; -import { metrics } from '../../../metrics/server'; import { canAccessRoomAsync } from '../../../authorization/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { metrics } from '../../../metrics/server'; +import { settings } from '../../../settings/server'; import { sendMessage } from '../functions/sendMessage'; const isParsedEmail = (email: ParsedMail): email is Required => 'date' in email && 'html' in email; diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js index d34322f4ceb9..17296d8f374a 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js @@ -1,22 +1,22 @@ +import { Subscriptions, Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import moment from 'moment'; -import { Subscriptions, Users } from '@rocket.chat/models'; +import { callbacks } from '../../../../lib/callbacks'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { Notification } from '../../../notification-queue/server/NotificationQueue'; import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; import { callJoinRoom, messageContainsHighlight, parseMessageTextPerUser, replaceMentionedUsernamesWithFullNames, } from '../functions/notifications'; +import { notifyDesktopUser, shouldNotifyDesktop } from '../functions/notifications/desktop'; import { getEmailData, shouldNotifyEmail } from '../functions/notifications/email'; import { getPushData, shouldNotifyMobile } from '../functions/notifications/mobile'; -import { notifyDesktopUser, shouldNotifyDesktop } from '../functions/notifications/desktop'; -import { Notification } from '../../../notification-queue/server/NotificationQueue'; import { getMentions } from './notifyUsersOnMessage'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; let TroubleshootDisableNotifications; diff --git a/apps/meteor/app/lib/server/lib/validateEmailDomain.js b/apps/meteor/app/lib/server/lib/validateEmailDomain.js index 18ff8404d8a0..3862dad83fef 100644 --- a/apps/meteor/app/lib/server/lib/validateEmailDomain.js +++ b/apps/meteor/app/lib/server/lib/validateEmailDomain.js @@ -3,16 +3,16 @@ import util from 'util'; import { Meteor } from 'meteor/meteor'; -import { emailDomainDefaultBlackList } from './defaultBlockedDomainsList'; -import { settings } from '../../../settings/server'; import { validateEmail } from '../../../../lib/emailValidator'; +import { settings } from '../../../settings/server'; +import { emailDomainDefaultBlackList } from './defaultBlockedDomainsList'; const dnsResolveMx = util.promisify(dns.resolveMx); let emailDomainBlackList = []; let emailDomainWhiteList = []; -settings.watch('Accounts_BlockedDomainsList', function (value) { +settings.watch('Accounts_BlockedDomainsList', (value) => { if (!value) { emailDomainBlackList = []; return; @@ -23,7 +23,7 @@ settings.watch('Accounts_BlockedDomainsList', function (value) { .filter(Boolean) .map((domain) => domain.trim()); }); -settings.watch('Accounts_AllowedDomainsList', function (value) { +settings.watch('Accounts_AllowedDomainsList', (value) => { if (!value) { emailDomainWhiteList = []; return; diff --git a/apps/meteor/app/lib/server/methods/addOAuthService.ts b/apps/meteor/app/lib/server/methods/addOAuthService.ts index 7d1a9079b40c..abf1b7035af1 100644 --- a/apps/meteor/app/lib/server/methods/addOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/addOAuthService.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { addOAuthService } from '../functions/addOAuthService'; diff --git a/apps/meteor/app/lib/server/methods/addUsersToRoom.ts b/apps/meteor/app/lib/server/methods/addUsersToRoom.ts index 29bed563771c..48b7f9db58f5 100644 --- a/apps/meteor/app/lib/server/methods/addUsersToRoom.ts +++ b/apps/meteor/app/lib/server/methods/addUsersToRoom.ts @@ -1,16 +1,16 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; import { api } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { addUserToRoom } from '../functions'; import { callbacks } from '../../../../lib/callbacks'; -import { Federation } from '../../../../server/services/federation/Federation'; import { i18n } from '../../../../server/lib/i18n'; +import { Federation } from '../../../../server/services/federation/Federation'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { addUserToRoom } from '../functions/addUserToRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/archiveRoom.ts b/apps/meteor/app/lib/server/methods/archiveRoom.ts index 773cdbb1ccf0..c30014f59c11 100644 --- a/apps/meteor/app/lib/server/methods/archiveRoom.ts +++ b/apps/meteor/app/lib/server/methods/archiveRoom.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; +import { isRegisterUser } from '@rocket.chat/core-typings'; import { Users, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { isRegisterUser } from '@rocket.chat/core-typings'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { archiveRoom } from '../functions/archiveRoom'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/blockUser.ts b/apps/meteor/app/lib/server/methods/blockUser.ts index 323f26e361ab..b65423edf25b 100644 --- a/apps/meteor/app/lib/server/methods/blockUser.ts +++ b/apps/meteor/app/lib/server/methods/blockUser.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Subscriptions, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts b/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts index d0b500432462..3f349a8b43e6 100644 --- a/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts +++ b/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts b/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts index 3b83f0d1963e..34ad02ddb2cf 100644 --- a/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts +++ b/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { checkUsernameAvailabilityWithValidation } from '../functions/checkUsernameAvailability'; import { RateLimiter } from '../lib'; diff --git a/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts b/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts index 23c3c81ebe1b..a724d1b38b2c 100644 --- a/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts +++ b/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { cleanRoomHistory } from '../functions/cleanRoomHistory'; diff --git a/apps/meteor/app/lib/server/methods/createChannel.ts b/apps/meteor/app/lib/server/methods/createChannel.ts index 82ec272c50e1..ff8182cec8c9 100644 --- a/apps/meteor/app/lib/server/methods/createChannel.ts +++ b/apps/meteor/app/lib/server/methods/createChannel.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ICreatedRoom } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { createRoom } from '../functions'; +import { createRoom } from '../functions/createRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts index 67bcb671e479..65298949a345 100644 --- a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts +++ b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ICreatedRoom } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { createRoom } from '../functions'; +import { createRoom } from '../functions/createRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/createToken.ts b/apps/meteor/app/lib/server/methods/createToken.ts index 441a10982de0..b16a61a7dacd 100644 --- a/apps/meteor/app/lib/server/methods/createToken.ts +++ b/apps/meteor/app/lib/server/methods/createToken.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/lib/server/methods/deleteMessage.ts b/apps/meteor/app/lib/server/methods/deleteMessage.ts index b15a74e781ae..a1b1000c06b8 100644 --- a/apps/meteor/app/lib/server/methods/deleteMessage.ts +++ b/apps/meteor/app/lib/server/methods/deleteMessage.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import type { IMessage } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { deleteMessageValidatingPermission } from '../functions/deleteMessage'; import { methodDeprecationLogger } from '../lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts index d13ec185a205..ed9929622c6d 100644 --- a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts +++ b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { Accounts } from 'meteor/accounts-base'; +import { Users } from '@rocket.chat/models'; import { SHA256 } from '@rocket.chat/sha256'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { Users } from '@rocket.chat/models'; +import { Accounts } from 'meteor/accounts-base'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; -import { deleteUser } from '../functions'; import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { trim } from '../../../../lib/utils/stringUtils'; +import { settings } from '../../../settings/server'; +import { deleteUser } from '../functions/deleteUser'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts b/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts index de7102e69886..80b5428efbef 100644 --- a/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts +++ b/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts @@ -2,7 +2,7 @@ import type { IMessage, RequiredField, SlashCommandPreviewItem } from '@rocket.c import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../../utils/server'; +import { slashCommands } from '../../../utils/server/slashCommand'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/filterBadWords.ts b/apps/meteor/app/lib/server/methods/filterBadWords.ts index 37fbf8701bf3..3db0ee76e997 100644 --- a/apps/meteor/app/lib/server/methods/filterBadWords.ts +++ b/apps/meteor/app/lib/server/methods/filterBadWords.ts @@ -1,10 +1,10 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import Filter from 'bad-words'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import Filter from 'bad-words'; -import type { IMessage } from '@rocket.chat/core-typings'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; const Dep = new Tracker.Dependency(); Meteor.startup(() => { diff --git a/apps/meteor/app/lib/server/methods/getChannelHistory.ts b/apps/meteor/app/lib/server/methods/getChannelHistory.ts index 278958518135..3c68fb7a2bf2 100644 --- a/apps/meteor/app/lib/server/methods/getChannelHistory.ts +++ b/apps/meteor/app/lib/server/methods/getChannelHistory.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import _ from 'underscore'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; import { canAccessRoomAsync } from '../../../authorization/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/lib/server/methods/getMessages.ts b/apps/meteor/app/lib/server/methods/getMessages.ts index 2a06535fac10..909e3d0c656b 100644 --- a/apps/meteor/app/lib/server/methods/getMessages.ts +++ b/apps/meteor/app/lib/server/methods/getMessages.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { IMessage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Messages } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; diff --git a/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts b/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts index f95c0ed96edc..75e4a4372f57 100644 --- a/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts +++ b/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { isRoomWithJoinCode } from '@rocket.chat/core-typings'; import { Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/lib/server/methods/getRoomRoles.ts b/apps/meteor/app/lib/server/methods/getRoomRoles.ts index 43c4325b060a..ac5fcc734d0f 100644 --- a/apps/meteor/app/lib/server/methods/getRoomRoles.ts +++ b/apps/meteor/app/lib/server/methods/getRoomRoles.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; import { getRoomRoles } from '../../../../server/lib/roles/getRoomRoles'; import { canAccessRoomAsync } from '../../../authorization/server'; +import { settings } from '../../../settings/server'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/getSingleMessage.ts b/apps/meteor/app/lib/server/methods/getSingleMessage.ts index d274299f9f51..1aaf2a652257 100644 --- a/apps/meteor/app/lib/server/methods/getSingleMessage.ts +++ b/apps/meteor/app/lib/server/methods/getSingleMessage.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { Messages } from '@rocket.chat/models'; import type { IMessage } from '@rocket.chat/core-typings'; +import { Messages } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; diff --git a/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts b/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts index 5a0cf2f736a2..dcb39c466063 100644 --- a/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts +++ b/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts @@ -2,7 +2,7 @@ import type { IMessage, RequiredField, SlashCommandPreviews } from '@rocket.chat import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../../utils/server'; +import { slashCommands } from '../../../utils/server/slashCommand'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/getUserRoles.ts b/apps/meteor/app/lib/server/methods/getUserRoles.ts index 0c62121523d0..cc7588276c33 100644 --- a/apps/meteor/app/lib/server/methods/getUserRoles.ts +++ b/apps/meteor/app/lib/server/methods/getUserRoles.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import { Authorization } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IUser, IRocketChatRecord } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts b/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts index 245fdcba9db6..5724449a5790 100644 --- a/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts +++ b/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts @@ -1,7 +1,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { generateUsernameSuggestion } from '../functions'; +import { generateUsernameSuggestion } from '../functions/getUsernameSuggestion'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts index 8445c08219ee..d00f6d30b314 100644 --- a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts +++ b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { saveUser } from '../functions'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; +import { saveUser } from '../functions/saveUser'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -13,7 +13,7 @@ declare module '@rocket.chat/ui-contexts' { } Meteor.methods({ - insertOrUpdateUser: twoFactorRequired(async function (userData) { + insertOrUpdateUser: twoFactorRequired(async (userData) => { check(userData, Object); if (!Meteor.userId()) { diff --git a/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts b/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts index fa8af706fa02..309ac4e449be 100644 --- a/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts +++ b/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { addUserToDefaultChannels } from '../functions'; +import { addUserToDefaultChannels } from '../functions/addUserToDefaultChannels'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/joinRoom.ts b/apps/meteor/app/lib/server/methods/joinRoom.ts index 1f1c58eceb37..355fd49916ae 100644 --- a/apps/meteor/app/lib/server/methods/joinRoom.ts +++ b/apps/meteor/app/lib/server/methods/joinRoom.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { IRoom, IRoomWithJoinCode, IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Rooms, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { canAccessRoomAsync } from '../../../authorization/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { addUserToRoom } from '../functions'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { addUserToRoom } from '../functions/addUserToRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/leaveRoom.ts b/apps/meteor/app/lib/server/methods/leaveRoom.ts index 09d9e1e72e80..ec1bb1638a21 100644 --- a/apps/meteor/app/lib/server/methods/leaveRoom.ts +++ b/apps/meteor/app/lib/server/methods/leaveRoom.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { IUser } from '@rocket.chat/core-typings'; import { Roles, Subscriptions, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { removeUserFromRoom } from '../functions'; -import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; +import { removeUserFromRoom } from '../functions/removeUserFromRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/refreshOAuthService.ts b/apps/meteor/app/lib/server/methods/refreshOAuthService.ts index 270bc1530ff7..9faa67f239a1 100644 --- a/apps/meteor/app/lib/server/methods/refreshOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/refreshOAuthService.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { ServiceConfiguration } from 'meteor/service-configuration'; import { Settings } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import { ServiceConfiguration } from 'meteor/service-configuration'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/lib/server/methods/removeOAuthService.ts b/apps/meteor/app/lib/server/methods/removeOAuthService.ts index 6c19b2259156..6d1bb688979d 100644 --- a/apps/meteor/app/lib/server/methods/removeOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/removeOAuthService.ts @@ -1,8 +1,8 @@ -import { capitalize } from '@rocket.chat/string-helpers'; -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { Settings } from '@rocket.chat/models'; +import { capitalize } from '@rocket.chat/string-helpers'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; @@ -61,6 +61,7 @@ Meteor.methods({ Settings.removeById(`Accounts_OAuth_Custom-${name}-channels_admin`), Settings.removeById(`Accounts_OAuth_Custom-${name}-map_channels`), Settings.removeById(`Accounts_OAuth_Custom-${name}-groups_channel_map`), + Settings.removeById(`Accounts_OAuth_Custom-${name}-merge_users_distinct_services`), ]); }, }); diff --git a/apps/meteor/app/lib/server/methods/saveCustomFields.ts b/apps/meteor/app/lib/server/methods/saveCustomFields.ts index ac048787feee..a509c97bdb85 100644 --- a/apps/meteor/app/lib/server/methods/saveCustomFields.ts +++ b/apps/meteor/app/lib/server/methods/saveCustomFields.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { RateLimiter } from '../lib'; import { saveCustomFields } from '../functions/saveCustomFields'; +import { RateLimiter } from '../lib'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/saveSetting.ts b/apps/meteor/app/lib/server/methods/saveSetting.ts index 7683162a49ff..d6cd62dc7470 100644 --- a/apps/meteor/app/lib/server/methods/saveSetting.ts +++ b/apps/meteor/app/lib/server/methods/saveSetting.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; +import type { SettingValue } from '@rocket.chat/core-typings'; import { Settings } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { SettingValue } from '@rocket.chat/core-typings'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync, hasAllPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { getSettingPermissionId } from '../../../authorization/lib'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; +import { getSettingPermissionId } from '../../../authorization/lib'; +import { hasPermissionAsync, hasAllPermissionAsync } from '../../../authorization/server/functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -16,7 +16,7 @@ declare module '@rocket.chat/ui-contexts' { } Meteor.methods({ - saveSetting: twoFactorRequired(async function (_id, value, editor) { + saveSetting: twoFactorRequired(async (_id, value, editor) => { const uid = Meteor.userId(); if (!uid) { throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', { diff --git a/apps/meteor/app/lib/server/methods/saveSettings.ts b/apps/meteor/app/lib/server/methods/saveSettings.ts index 063935e7417b..776897ce4a58 100644 --- a/apps/meteor/app/lib/server/methods/saveSettings.ts +++ b/apps/meteor/app/lib/server/methods/saveSettings.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { Settings } from '@rocket.chat/models'; import type { ISetting } from '@rocket.chat/core-typings'; import { isSettingCode } from '@rocket.chat/core-typings'; +import { Settings } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { getSettingPermissionId } from '../../../authorization/lib'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; +import { getSettingPermissionId } from '../../../authorization/lib'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -31,67 +31,70 @@ const validJSON = Match.Where((value: string) => { }); Meteor.methods({ - saveSettings: twoFactorRequired(async function ( - params: { - _id: ISetting['_id']; - value: ISetting['value']; - }[] = [], - ) { - const uid = Meteor.userId(); - const settingsNotAllowed: ISetting['_id'][] = []; - if (uid === null) { - throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', { - method: 'saveSetting', - }); - } - const editPrivilegedSetting = await hasPermissionAsync(uid, 'edit-privileged-setting'); - const manageSelectedSettings = await hasPermissionAsync(uid, 'manage-selected-settings'); + saveSettings: twoFactorRequired( + async ( + params: { + _id: ISetting['_id']; + value: ISetting['value']; + }[] = [], + ) => { + const uid = Meteor.userId(); + const settingsNotAllowed: ISetting['_id'][] = []; + if (uid === null) { + throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', { + method: 'saveSetting', + }); + } + const editPrivilegedSetting = await hasPermissionAsync(uid, 'edit-privileged-setting'); + const manageSelectedSettings = await hasPermissionAsync(uid, 'manage-selected-settings'); - await Promise.all( - params.map(async ({ _id, value }) => { - // Verify the _id passed in is a string. - check(_id, String); - if (!editPrivilegedSetting && !(manageSelectedSettings && (await hasPermissionAsync(uid, getSettingPermissionId(_id))))) { - return settingsNotAllowed.push(_id); - } + await Promise.all( + params.map(async ({ _id, value }) => { + // Verify the _id passed in is a string. + check(_id, String); + if (!editPrivilegedSetting && !(manageSelectedSettings && (await hasPermissionAsync(uid, getSettingPermissionId(_id))))) { + return settingsNotAllowed.push(_id); + } - const setting = await Settings.findOneById(_id); - // Verify the value is what it should be - switch (setting?.type) { - case 'roomPick': - check(value, Match.OneOf([Object], '')); - break; - case 'boolean': - check(value, Boolean); - break; - case 'int': - check(value, Number); - break; - case 'multiSelect': - check(value, Array); - break; - case 'code': - check(value, String); - if (isSettingCode(setting) && setting.code === 'application/json') { - check(value, validJSON); - } - break; - default: - check(value, String); - break; - } - }), - ); + const setting = await Settings.findOneById(_id); + // Verify the value is what it should be + switch (setting?.type) { + case 'roomPick': + check(value, Match.OneOf([Object], '')); + break; + case 'boolean': + check(value, Boolean); + break; + case 'int': + check(value, Number); + break; + case 'multiSelect': + check(value, Array); + break; + case 'code': + check(value, String); + if (isSettingCode(setting) && setting.code === 'application/json') { + check(value, validJSON); + } + break; + default: + check(value, String); + break; + } + }), + ); - if (settingsNotAllowed.length) { - throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', { - method: 'saveSettings', - settingIds: settingsNotAllowed, - }); - } + if (settingsNotAllowed.length) { + throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', { + method: 'saveSettings', + settingIds: settingsNotAllowed, + }); + } - await Promise.all(params.map(({ _id, value }) => Settings.updateValueById(_id, value))); + await Promise.all(params.map(({ _id, value }) => Settings.updateValueById(_id, value))); - return true; - }, {}), + return true; + }, + {}, + ), }); diff --git a/apps/meteor/app/lib/server/methods/sendMessage.ts b/apps/meteor/app/lib/server/methods/sendMessage.ts index 9d764c05fb6c..ebdcdfd43d9b 100644 --- a/apps/meteor/app/lib/server/methods/sendMessage.ts +++ b/apps/meteor/app/lib/server/methods/sendMessage.ts @@ -1,21 +1,21 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import moment from 'moment'; import { api } from '@rocket.chat/core-services'; -import { Messages, Users } from '@rocket.chat/models'; import type { AtLeast, IMessage, IUser } from '@rocket.chat/core-typings'; +import { Messages, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; +import { i18n } from '../../../../server/lib/i18n'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { metrics } from '../../../metrics/server'; import { settings } from '../../../settings/server'; -import { sendMessage } from '../functions'; +import { sendMessage } from '../functions/sendMessage'; import { RateLimiter } from '../lib'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { i18n } from '../../../../server/lib/i18n'; -export async function executeSendMessage(uid: IUser['_id'], message: AtLeast) { +export async function executeSendMessage(uid: IUser['_id'], message: AtLeast, previewUrls?: string[]) { if (message.tshow && !message.tmid) { throw new Meteor.Error('invalid-params', 'tshow provided but missing tmid', { method: 'sendMessage', @@ -82,7 +82,7 @@ export async function executeSendMessage(uid: IUser['_id'], message: AtLeast): any; + sendMessage(message: AtLeast, previewUrls?: string[]): any; } } Meteor.methods({ - sendMessage(message) { + sendMessage(message, previewUrls) { check(message, Object); const uid = Meteor.userId(); @@ -118,7 +118,7 @@ Meteor.methods({ } try { - return executeSendMessage(uid, message); + return executeSendMessage(uid, message, previewUrls); } catch (error: any) { if ((error.error || error.message) === 'error-not-allowed') { throw new Meteor.Error(error.error || error.message, error.reason, { diff --git a/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts b/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts index 6389742f7a7f..2ede129dd380 100644 --- a/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts +++ b/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { Meteor } from 'meteor/meteor'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/lib/server/methods/setAdminStatus.ts b/apps/meteor/app/lib/server/methods/setAdminStatus.ts index abc388aec20a..42ffdc91f85e 100644 --- a/apps/meteor/app/lib/server/methods/setAdminStatus.ts +++ b/apps/meteor/app/lib/server/methods/setAdminStatus.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { isUserFederated } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/lib/server/methods/setEmail.ts b/apps/meteor/app/lib/server/methods/setEmail.ts index 559544dcb683..a172eba72555 100644 --- a/apps/meteor/app/lib/server/methods/setEmail.ts +++ b/apps/meteor/app/lib/server/methods/setEmail.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; -import { setEmail } from '../functions'; +import { setEmail } from '../functions/setEmail'; import { RateLimiter } from '../lib'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/lib/server/methods/setRealName.ts b/apps/meteor/app/lib/server/methods/setRealName.ts index c96940310caf..2a55bd102172 100644 --- a/apps/meteor/app/lib/server/methods/setRealName.ts +++ b/apps/meteor/app/lib/server/methods/setRealName.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; -import { setRealName } from '../functions'; +import { setRealName } from '../functions/setRealName'; import { RateLimiter } from '../lib'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/lib/server/methods/setUsername.ts b/apps/meteor/app/lib/server/methods/setUsername.ts index 06e3db19e83a..900848f37783 100644 --- a/apps/meteor/app/lib/server/methods/setUsername.ts +++ b/apps/meteor/app/lib/server/methods/setUsername.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { RateLimiter } from '../lib'; import { setUsernameWithValidation } from '../functions/setUsername'; +import { RateLimiter } from '../lib'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/unarchiveRoom.ts b/apps/meteor/app/lib/server/methods/unarchiveRoom.ts index bf7643799e74..0f9349365f4f 100644 --- a/apps/meteor/app/lib/server/methods/unarchiveRoom.ts +++ b/apps/meteor/app/lib/server/methods/unarchiveRoom.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { Users, Rooms } from '@rocket.chat/models'; import { isRegisterUser } from '@rocket.chat/core-typings'; +import { Users, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { unarchiveRoom } from '../functions'; +import { unarchiveRoom } from '../functions/unarchiveRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/unblockUser.ts b/apps/meteor/app/lib/server/methods/unblockUser.ts index 24a941ff601e..6c8a9d486bab 100644 --- a/apps/meteor/app/lib/server/methods/unblockUser.ts +++ b/apps/meteor/app/lib/server/methods/unblockUser.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Subscriptions } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/updateMessage.ts b/apps/meteor/app/lib/server/methods/updateMessage.ts index e86451fef429..a34492400130 100644 --- a/apps/meteor/app/lib/server/methods/updateMessage.ts +++ b/apps/meteor/app/lib/server/methods/updateMessage.ts @@ -1,108 +1,113 @@ -import { Meteor } from 'meteor/meteor'; +import type { IEditedMessage, IMessage, IUser, AtLeast } from '@rocket.chat/core-typings'; +import { Messages, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import moment from 'moment'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IEditedMessage, IMessage } from '@rocket.chat/core-typings'; -import { Messages, Users } from '@rocket.chat/models'; -import { settings } from '../../../settings/server'; import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { updateMessage } from '../functions'; +import { settings } from '../../../settings/server'; +import { updateMessage } from '../functions/updateMessage'; const allowedEditedFields = ['tshow', 'alias', 'attachments', 'avatar', 'emoji', 'msg']; -declare module '@rocket.chat/ui-contexts' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface ServerMethods { - updateMessage(message: IEditedMessage): void; +export async function executeUpdateMessage(uid: IUser['_id'], message: AtLeast, previewUrls?: string[]) { + const originalMessage = await Messages.findOneById(message._id); + if (!originalMessage?._id) { + return; } -} -Meteor.methods({ - async updateMessage(message: IEditedMessage) { - check(message, Match.ObjectIncluding({ _id: String })); + Object.entries(message).forEach(([key, value]) => { + if (!allowedEditedFields.includes(key) && value !== originalMessage[key as keyof IMessage]) { + throw new Meteor.Error('error-invalid-update-key', `Cannot update the message ${key}`, { + method: 'updateMessage', + }); + } + }); - const uid = Meteor.userId(); + const msgText = originalMessage?.attachments?.[0]?.description ?? originalMessage.msg; + if (msgText === message.msg && !previewUrls) { + return; + } - if (!uid) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateMessage' }); - } + if (!!message.tmid && originalMessage._id === message.tmid) { + throw new Meteor.Error('error-message-same-as-tmid', 'Cannot set tmid the same as the _id', { + method: 'updateMessage', + }); + } - const originalMessage = await Messages.findOneById(message._id); - if (!originalMessage?._id) { - return; - } + if (!originalMessage.tmid && !!message.tmid) { + throw new Meteor.Error('error-message-change-to-thread', 'Cannot update message to a thread', { method: 'updateMessage' }); + } + + const _hasPermission = await hasPermissionAsync(uid, 'edit-message', message.rid); + const editAllowed = settings.get('Message_AllowEditing'); + const editOwn = originalMessage.u && originalMessage.u._id === uid; - Object.entries(message).forEach(([key, value]) => { - if (!allowedEditedFields.includes(key) && value !== originalMessage[key as keyof IMessage]) { - throw new Meteor.Error('error-invalid-update-key', `Cannot update the message ${key}`, { - method: 'updateMessage', - }); - } + if (!_hasPermission && (!editAllowed || !editOwn)) { + throw new Meteor.Error('error-action-not-allowed', 'Message editing not allowed', { + method: 'updateMessage', + action: 'Message_editing', }); + } - const msgText = originalMessage?.attachments?.[0]?.description ?? originalMessage.msg; - if (msgText === message.msg) { - return; - } + const blockEditInMinutes = settings.get('Message_AllowEditing_BlockEditInMinutes'); + const bypassBlockTimeLimit = await hasPermissionAsync(uid, 'bypass-time-limit-edit-and-delete'); + + if (!bypassBlockTimeLimit && Match.test(blockEditInMinutes, Number) && blockEditInMinutes !== 0) { + let currentTsDiff = 0; + let msgTs; - if (!!message.tmid && originalMessage._id === message.tmid) { - throw new Meteor.Error('error-message-same-as-tmid', 'Cannot set tmid the same as the _id', { + if (originalMessage.ts instanceof Date || Match.test(originalMessage.ts, Number)) { + msgTs = moment(originalMessage.ts); + } + if (msgTs) { + currentTsDiff = moment().diff(msgTs, 'minutes'); + } + if (currentTsDiff >= blockEditInMinutes) { + throw new Meteor.Error('error-message-editing-blocked', 'Message editing is blocked', { method: 'updateMessage', }); } + } - if (!originalMessage.tmid && !!message.tmid) { - throw new Meteor.Error('error-message-change-to-thread', 'Cannot update message to a thread', { method: 'updateMessage' }); - } + const user = await Users.findOneById(uid); + if (!user) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateMessage' }); + } + await canSendMessageAsync(message.rid, { uid: user._id, username: user.username ?? undefined, ...user }); - const _hasPermission = await hasPermissionAsync(uid, 'edit-message', message.rid); - const editAllowed = settings.get('Message_AllowEditing'); - const editOwn = originalMessage.u && originalMessage.u._id === uid; + // It is possible to have an empty array as the attachments property, so ensure both things exist + if (originalMessage.attachments && originalMessage.attachments.length > 0 && originalMessage.attachments[0].description !== undefined) { + originalMessage.attachments[0].description = message.msg; + message.attachments = originalMessage.attachments; + message.msg = originalMessage.msg; + } - if (!_hasPermission && (!editAllowed || !editOwn)) { - throw new Meteor.Error('error-action-not-allowed', 'Message editing not allowed', { - method: 'updateMessage', - action: 'Message_editing', - }); - } + message.u = originalMessage.u; - const blockEditInMinutes = settings.get('Message_AllowEditing_BlockEditInMinutes'); - const bypassBlockTimeLimit = await hasPermissionAsync(uid, 'bypass-time-limit-edit-and-delete'); - - if (!bypassBlockTimeLimit && Match.test(blockEditInMinutes, Number) && blockEditInMinutes !== 0) { - let currentTsDiff = 0; - let msgTs; - - if (originalMessage.ts instanceof Date || Match.test(originalMessage.ts, Number)) { - msgTs = moment(originalMessage.ts); - } - if (msgTs) { - currentTsDiff = moment().diff(msgTs, 'minutes'); - } - if (currentTsDiff >= blockEditInMinutes) { - throw new Meteor.Error('error-message-editing-blocked', 'Message editing is blocked', { - method: 'updateMessage', - }); - } - } + return updateMessage(message, user, originalMessage, previewUrls); +} - const user = await Users.findOneById(uid); - if (!user) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateMessage' }); - } - await canSendMessageAsync(message.rid, { uid: user._id, username: user.username ?? undefined, ...user }); +declare module '@rocket.chat/ui-contexts' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface ServerMethods { + updateMessage(message: IEditedMessage, previewUrls?: string[]): void; + } +} - // It is possible to have an empty array as the attachments property, so ensure both things exist - if (originalMessage.attachments && originalMessage.attachments.length > 0 && originalMessage.attachments[0].description !== undefined) { - originalMessage.attachments[0].description = message.msg; - message.attachments = originalMessage.attachments; - message.msg = originalMessage.msg; - } +Meteor.methods({ + async updateMessage(message: IEditedMessage, previewUrls?: string[]) { + check(message, Match.ObjectIncluding({ _id: String })); + check(previewUrls, Match.Maybe([String])); + + const uid = Meteor.userId(); - message.u = originalMessage.u; + if (!uid) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateMessage' }); + } - return updateMessage(message, user, originalMessage); + return executeUpdateMessage(uid, message, previewUrls); }, }); diff --git a/apps/meteor/app/lib/server/oauth/facebook.js b/apps/meteor/app/lib/server/oauth/facebook.js index e5409f173b90..e60dff901d69 100644 --- a/apps/meteor/app/lib/server/oauth/facebook.js +++ b/apps/meteor/app/lib/server/oauth/facebook.js @@ -1,9 +1,9 @@ import crypto from 'crypto'; +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Match, check } from 'meteor/check'; -import _ from 'underscore'; import { OAuth } from 'meteor/oauth'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import _ from 'underscore'; import { registerAccessTokenService } from './oauth'; @@ -37,7 +37,7 @@ const getIdentity = async function (accessToken, fields, secret) { } }; -registerAccessTokenService('facebook', async function (options) { +registerAccessTokenService('facebook', async (options) => { check( options, Match.ObjectIncluding({ diff --git a/apps/meteor/app/lib/server/oauth/google.js b/apps/meteor/app/lib/server/oauth/google.js index dce2f484f594..34c626d6a941 100644 --- a/apps/meteor/app/lib/server/oauth/google.js +++ b/apps/meteor/app/lib/server/oauth/google.js @@ -1,7 +1,7 @@ +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Match, check } from 'meteor/check'; -import _ from 'underscore'; import { Google } from 'meteor/google-oauth'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import _ from 'underscore'; import { registerAccessTokenService } from './oauth'; @@ -41,7 +41,7 @@ async function getScopes(accessToken) { } } -registerAccessTokenService('google', async function (options) { +registerAccessTokenService('google', async (options) => { check( options, Match.ObjectIncluding({ diff --git a/apps/meteor/app/lib/server/oauth/oauth.js b/apps/meteor/app/lib/server/oauth/oauth.js index c759f4e1b49f..2618a6e7a569 100644 --- a/apps/meteor/app/lib/server/oauth/oauth.js +++ b/apps/meteor/app/lib/server/oauth/oauth.js @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { Accounts } from 'meteor/accounts-base'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; import _ from 'underscore'; @@ -15,7 +15,7 @@ export const registerAccessTokenService = function (serviceName, handleAccessTok // Listen to calls to `login` with an oauth option set. This is where // users actually get logged in to meteor via oauth. -Accounts.registerLoginHandler(async function (options) { +Accounts.registerLoginHandler(async (options) => { if (!options.accessToken) { return undefined; // don't handle } diff --git a/apps/meteor/app/lib/server/oauth/proxy.js b/apps/meteor/app/lib/server/oauth/proxy.js index 1ea4db77f257..c66143d8b029 100644 --- a/apps/meteor/app/lib/server/oauth/proxy.js +++ b/apps/meteor/app/lib/server/oauth/proxy.js @@ -1,9 +1,9 @@ -import _ from 'underscore'; import { OAuth } from 'meteor/oauth'; +import _ from 'underscore'; import { settings } from '../../../settings/server'; -OAuth._redirectUri = _.wrap(OAuth._redirectUri, function (func, serviceName, ...args) { +OAuth._redirectUri = _.wrap(OAuth._redirectUri, (func, serviceName, ...args) => { const proxy = settings.get('Accounts_OAuth_Proxy_services').replace(/\s/g, '').split(','); if (proxy.includes(serviceName)) { return `${settings.get('Accounts_OAuth_Proxy_host')}/oauth_redirect`; diff --git a/apps/meteor/app/lib/server/oauth/twitter.js b/apps/meteor/app/lib/server/oauth/twitter.js index c214ee4a34e9..b3394332a037 100644 --- a/apps/meteor/app/lib/server/oauth/twitter.js +++ b/apps/meteor/app/lib/server/oauth/twitter.js @@ -24,7 +24,7 @@ const getIdentity = async function (accessToken, appId, appSecret, accessTokenSe } }; -registerAccessTokenService('twitter', async function (options) { +registerAccessTokenService('twitter', async (options) => { check( options, Match.ObjectIncluding({ diff --git a/apps/meteor/app/lib/server/startup/filterATAllTag.ts b/apps/meteor/app/lib/server/startup/filterATAllTag.ts index 9bc7329d0b0f..dfa3b1b1d84a 100644 --- a/apps/meteor/app/lib/server/startup/filterATAllTag.ts +++ b/apps/meteor/app/lib/server/startup/filterATAllTag.ts @@ -1,17 +1,17 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import moment from 'moment'; import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import { isEditedMessage } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; +import _ from 'underscore'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { callbacks } from '../../../../lib/callbacks'; import { i18n } from '../../../../server/lib/i18n'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; callbacks.add( 'beforeSaveMessage', - async function (message) { + async (message) => { // If the message was edited, or is older than 60 seconds (imported) // the notifications will be skipped, so we can also skip this validation if (isEditedMessage(message) || (message.ts && Math.abs(moment(message.ts).diff(moment())) > 60000)) { @@ -46,6 +46,6 @@ callbacks.add( return message; }, - callbacks.priority.HIGH, + callbacks.priority.MEDIUM, 'filterATAllTag', ); diff --git a/apps/meteor/app/lib/server/startup/filterATHereTag.ts b/apps/meteor/app/lib/server/startup/filterATHereTag.ts index f4fcaa11bf9b..305f03455d81 100644 --- a/apps/meteor/app/lib/server/startup/filterATHereTag.ts +++ b/apps/meteor/app/lib/server/startup/filterATHereTag.ts @@ -1,17 +1,17 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import moment from 'moment'; import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import { isEditedMessage } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; +import _ from 'underscore'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { callbacks } from '../../../../lib/callbacks'; import { i18n } from '../../../../server/lib/i18n'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; callbacks.add( 'beforeSaveMessage', - async function (message) { + async (message) => { // If the message was edited, or is older than 60 seconds (imported) // the notifications will be skipped, so we can also skip this validation if (isEditedMessage(message) || (message.ts && Math.abs(moment(message.ts).diff(moment())) > 60000)) { @@ -46,6 +46,6 @@ callbacks.add( return message; }, - callbacks.priority.HIGH, + callbacks.priority.MEDIUM, 'filterATHereTag', ); diff --git a/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js b/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js index e69d3c900ead..b01ef2f9fb0c 100644 --- a/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js +++ b/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js @@ -1,8 +1,8 @@ +import { Logger } from '@rocket.chat/logger'; import { ServiceConfiguration } from 'meteor/service-configuration'; import _ from 'underscore'; import { CustomOAuth } from '../../../custom-oauth/server/custom_oauth_server'; -import { Logger } from '../../../logger/server'; import { settings } from '../../../settings/server'; import { addOAuthService } from '../functions/addOAuthService'; @@ -97,7 +97,7 @@ async function _OAuthServicesUpdate() { if (serviceName === 'Linkedin') { data.clientConfig = { - requestPermissions: ['r_liteprofile', 'r_emailaddress'], + requestPermissions: ['openid', 'email', 'profile'], }; } @@ -138,11 +138,11 @@ async function OAuthServicesRemove(_id) { }); } -settings.watchByRegex(/^Accounts_OAuth_.+/, function () { +settings.watchByRegex(/^Accounts_OAuth_.+/, () => { return OAuthServicesUpdate(); // eslint-disable-line new-cap }); -settings.watchByRegex(/^Accounts_OAuth_Custom-[a-z0-9_]+/, function (key, value) { +settings.watchByRegex(/^Accounts_OAuth_Custom-[a-z0-9_]+/, (key, value) => { if (!value) { return OAuthServicesRemove(key); // eslint-disable-line new-cap } diff --git a/apps/meteor/app/lib/server/startup/rateLimiter.js b/apps/meteor/app/lib/server/startup/rateLimiter.js index 5a7e8f07367b..a1ddfe87886c 100644 --- a/apps/meteor/app/lib/server/startup/rateLimiter.js +++ b/apps/meteor/app/lib/server/startup/rateLimiter.js @@ -1,12 +1,12 @@ -import _ from 'underscore'; -import { Meteor } from 'meteor/meteor'; +import { Logger } from '@rocket.chat/logger'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { Meteor } from 'meteor/meteor'; import { RateLimiter } from 'meteor/rate-limit'; +import _ from 'underscore'; -import { settings } from '../../../settings/server'; -import { metrics } from '../../../metrics/server'; -import { Logger } from '../../../logger/server'; import { sleep } from '../../../../lib/utils/sleep'; +import { metrics } from '../../../metrics/server'; +import { settings } from '../../../settings/server'; const logger = new Logger('RateLimiter'); @@ -62,7 +62,7 @@ RateLimiter.prototype.check = function (input) { }; const matchedRules = self._findAllMatchingRules(input); - _.each(matchedRules, function (rule) { + _.each(matchedRules, (rule) => { // ==== BEGIN OVERRIDE ==== const callbackReply = { allowed: true, diff --git a/apps/meteor/app/lib/server/startup/robots.js b/apps/meteor/app/lib/server/startup/robots.js index 0cc93221fbe5..2225b2b725e0 100644 --- a/apps/meteor/app/lib/server/startup/robots.js +++ b/apps/meteor/app/lib/server/startup/robots.js @@ -3,8 +3,8 @@ import { WebApp } from 'meteor/webapp'; import { settings } from '../../../settings/server'; -Meteor.startup(function () { - return WebApp.connectHandlers.use('/robots.txt', function (req, res /* , next*/) { +Meteor.startup(() => { + return WebApp.connectHandlers.use('/robots.txt', (req, res /* , next*/) => { res.writeHead(200); res.end(settings.get('Robot_Instructions_File_Content')); }); diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadCdnPrefix.js b/apps/meteor/app/lib/server/startup/settingsOnLoadCdnPrefix.js index 38d8f2134dcb..9dc8ef164b86 100644 --- a/apps/meteor/app/lib/server/startup/settingsOnLoadCdnPrefix.js +++ b/apps/meteor/app/lib/server/startup/settingsOnLoadCdnPrefix.js @@ -6,21 +6,21 @@ import { settings } from '../../../settings/server'; function testWebAppInternals(fn) { typeof WebAppInternals !== 'undefined' && fn(WebAppInternals); } -settings.change('CDN_PREFIX', function (value) { +settings.change('CDN_PREFIX', (value) => { const useForAll = settings.get('CDN_PREFIX_ALL'); if (value && typeof value.valueOf() === 'string' && value.trim() && useForAll) { return testWebAppInternals((WebAppInternals) => WebAppInternals.setBundledJsCssPrefix(value)); } }); -settings.change('CDN_JSCSS_PREFIX', function (value) { +settings.change('CDN_JSCSS_PREFIX', (value) => { const useForAll = settings.get('CDN_PREFIX_ALL'); if (value && typeof value.valueOf() === 'string' && value.trim() && !useForAll) { return testWebAppInternals((WebAppInternals) => WebAppInternals.setBundledJsCssPrefix(value)); } }); -Meteor.startup(function () { +Meteor.startup(() => { const cdnValue = settings.get('CDN_PREFIX'); const useForAll = settings.get('CDN_PREFIX_ALL'); const cdnJsCss = settings.get('CDN_JSCSS_PREFIX'); diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts index 9dcf1447d761..67776c7f3d91 100644 --- a/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts +++ b/apps/meteor/app/lib/server/startup/settingsOnLoadDirectReply.ts @@ -1,11 +1,11 @@ import _ from 'underscore'; +import { logger } from '../../../../server/features/EmailInbox/logger'; import { settings } from '../../../settings/server'; import { DirectReplyIMAPInterceptor, POP3Helper } from '../lib/interceptDirectReplyEmails.js'; -import { logger } from '../../../../server/features/EmailInbox/logger'; let client: DirectReplyIMAPInterceptor | POP3Helper | undefined; -const startEmailInterceptor = _.debounce(async function () { +const startEmailInterceptor = _.debounce(async () => { logger.info('Email Interceptor...'); const protocol = settings.get('Direct_Reply_Protocol'); diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadSMTP.ts b/apps/meteor/app/lib/server/startup/settingsOnLoadSMTP.ts index 06d2196f2c77..e015fd578d87 100644 --- a/apps/meteor/app/lib/server/startup/settingsOnLoadSMTP.ts +++ b/apps/meteor/app/lib/server/startup/settingsOnLoadSMTP.ts @@ -1,32 +1,29 @@ -import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; -settings.watchMultiple( - ['SMTP_Host', 'SMTP_Port', 'SMTP_Username', 'SMTP_Password', 'SMTP_Protocol', 'SMTP_Pool', 'SMTP_IgnoreTLS'], - function () { - SystemLogger.info('Updating process.env.MAIL_URL'); +settings.watchMultiple(['SMTP_Host', 'SMTP_Port', 'SMTP_Username', 'SMTP_Password', 'SMTP_Protocol', 'SMTP_Pool', 'SMTP_IgnoreTLS'], () => { + SystemLogger.info('Updating process.env.MAIL_URL'); - if (!settings.get('SMTP_Host')) { - process.env.MAIL_URL = undefined; - return; - } + if (!settings.get('SMTP_Host')) { + delete process.env.MAIL_URL; + return; + } - process.env.MAIL_URL = `${settings.get('SMTP_Protocol')}://`; + process.env.MAIL_URL = `${settings.get('SMTP_Protocol')}://`; - if (settings.get('SMTP_Username') && settings.get('SMTP_Password')) { - process.env.MAIL_URL += `${encodeURIComponent(settings.get('SMTP_Username'))}:${encodeURIComponent(settings.get('SMTP_Password'))}@`; - } + if (settings.get('SMTP_Username') && settings.get('SMTP_Password')) { + process.env.MAIL_URL += `${encodeURIComponent(settings.get('SMTP_Username'))}:${encodeURIComponent(settings.get('SMTP_Password'))}@`; + } - process.env.MAIL_URL += encodeURIComponent(settings.get('SMTP_Host')); + process.env.MAIL_URL += encodeURIComponent(settings.get('SMTP_Host')); - if (settings.get('SMTP_Port')) { - process.env.MAIL_URL += `:${parseInt(settings.get('SMTP_Port'))}`; - } + if (settings.get('SMTP_Port')) { + process.env.MAIL_URL += `:${parseInt(settings.get('SMTP_Port'))}`; + } - process.env.MAIL_URL += `?pool=${settings.get('SMTP_Pool')}`; + process.env.MAIL_URL += `?pool=${settings.get('SMTP_Pool')}`; - if (settings.get('SMTP_Protocol') === 'smtp' && settings.get('SMTP_IgnoreTLS')) { - process.env.MAIL_URL += '&secure=false&ignoreTLS=true'; - } - }, -); + if (settings.get('SMTP_Protocol') === 'smtp' && settings.get('SMTP_IgnoreTLS')) { + process.env.MAIL_URL += '&secure=false&ignoreTLS=true'; + } +}); diff --git a/apps/meteor/app/lib/server/startup/settingsOnLoadSiteUrl.ts b/apps/meteor/app/lib/server/startup/settingsOnLoadSiteUrl.ts index 739a87fc5342..231e41cd4a10 100644 --- a/apps/meteor/app/lib/server/startup/settingsOnLoadSiteUrl.ts +++ b/apps/meteor/app/lib/server/startup/settingsOnLoadSiteUrl.ts @@ -8,7 +8,7 @@ export let hostname: string; settings.watch( 'Site_Url', // Needed as WebAppInternals.generateBoilerplate needs to be called in a fiber - Meteor.bindEnvironment(function (value) { + Meteor.bindEnvironment((value) => { if (value == null || value.trim() === '') { return; } diff --git a/apps/meteor/app/livechat/client/externalFrame/ExternalFrameContainer.tsx b/apps/meteor/app/livechat/client/externalFrame/ExternalFrameContainer.tsx index 558c418b2240..5b37ee8de2d2 100644 --- a/apps/meteor/app/livechat/client/externalFrame/ExternalFrameContainer.tsx +++ b/apps/meteor/app/livechat/client/externalFrame/ExternalFrameContainer.tsx @@ -1,10 +1,10 @@ -import React, { useMemo } from 'react'; import { useSetting, useUserId } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; +import React, { useMemo } from 'react'; -import { encrypt, getKeyFromString } from './crypto'; import { useRoom } from '../../../../client/views/room/contexts/RoomContext'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { encrypt, getKeyFromString } from './crypto'; const ExternalFrameContainer = () => { const uid = useUserId(); diff --git a/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts b/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts index 6b8b804b5d16..eb0742813c1f 100644 --- a/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts +++ b/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts @@ -1,8 +1,8 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { generateKey } from './crypto'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { generateKey } from './crypto'; Meteor.methods({ async omnichannelExternalFrameGenerateKey() { diff --git a/apps/meteor/app/livechat/client/externalFrame/index.ts b/apps/meteor/app/livechat/client/externalFrame/index.ts index 33c0e0a88ca5..5f05d53f1ccc 100644 --- a/apps/meteor/app/livechat/client/externalFrame/index.ts +++ b/apps/meteor/app/livechat/client/externalFrame/index.ts @@ -1,2 +1 @@ import './generateNewKey'; -import './tabBar'; diff --git a/apps/meteor/app/livechat/client/externalFrame/tabBar.ts b/apps/meteor/app/livechat/client/externalFrame/tabBar.ts deleted file mode 100644 index 07e21d638fa6..000000000000 --- a/apps/meteor/app/livechat/client/externalFrame/tabBar.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { lazy, useMemo } from 'react'; -import { useSetting } from '@rocket.chat/ui-contexts'; - -import { addAction } from '../../../../client/views/room/lib/Toolbox'; - -const template = lazy(() => import('./ExternalFrameContainer')); - -addAction('omnichannel-external-frame', () => { - const enabled = useSetting('Omnichannel_External_Frame_Enabled'); - - return useMemo( - () => - enabled - ? { - groups: ['live'], - id: 'omnichannel-external-frame', - title: 'Omnichannel_External_Frame', - icon: 'cube', - template, - order: -1, - } - : null, - [enabled], - ); -}); diff --git a/apps/meteor/app/livechat/client/index.ts b/apps/meteor/app/livechat/client/index.ts index cd78fbfed7fb..84be06f4eb16 100644 --- a/apps/meteor/app/livechat/client/index.ts +++ b/apps/meteor/app/livechat/client/index.ts @@ -1,7 +1,6 @@ import '../lib/messageTypes'; import './voip'; import './ui'; -import './tabBar'; import './startup/notifyUnreadRooms'; import './stylesheets/livechat.css'; import './externalFrame'; diff --git a/apps/meteor/app/livechat/client/lib/chartHandler.ts b/apps/meteor/app/livechat/client/lib/chartHandler.ts index a432f4c5441c..19c1a004ca22 100644 --- a/apps/meteor/app/livechat/client/lib/chartHandler.ts +++ b/apps/meteor/app/livechat/client/lib/chartHandler.ts @@ -142,7 +142,7 @@ export const drawLineChart = async ( const datasets: ChartDataSet[] = []; - chartLabels.forEach(function (chartLabel: string, index: number) { + chartLabels.forEach((chartLabel: string, index: number) => { datasets.push({ label: t(chartLabel), // chart label data: dataSets[index], // data points corresponding to data labels, x-axis points diff --git a/apps/meteor/app/livechat/client/lib/stream/queueManager.ts b/apps/meteor/app/livechat/client/lib/stream/queueManager.ts index f7cf4c485cce..28d09958535a 100644 --- a/apps/meteor/app/livechat/client/lib/stream/queueManager.ts +++ b/apps/meteor/app/livechat/client/lib/stream/queueManager.ts @@ -1,9 +1,9 @@ import type { ILivechatDepartment, ILivechatInquiryRecord, IOmnichannelAgent } from '@rocket.chat/core-typings'; -import { LivechatInquiry } from '../../collections/LivechatInquiry'; -import { callWithErrorHandling } from '../../../../../client/lib/utils/callWithErrorHandling'; import { queryClient } from '../../../../../client/lib/queryClient'; +import { callWithErrorHandling } from '../../../../../client/lib/utils/callWithErrorHandling'; import { sdk } from '../../../../utils/client/lib/SDKClient'; +import { LivechatInquiry } from '../../collections/LivechatInquiry'; const departments = new Set(); diff --git a/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js b/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js index f730925f50d9..3aaace847f69 100644 --- a/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js +++ b/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js @@ -1,11 +1,11 @@ +import { Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { Users } from '@rocket.chat/models'; +import { CustomSounds } from '../../../custom-sounds/client'; +import { Subscriptions } from '../../../models/client'; import { settings } from '../../../settings/client'; import { getUserPreference } from '../../../utils/client'; -import { Subscriptions } from '../../../models/client'; -import { CustomSounds } from '../../../custom-sounds/client'; let audio = null; diff --git a/apps/meteor/app/livechat/client/tabBar.ts b/apps/meteor/app/livechat/client/tabBar.ts deleted file mode 100644 index 9a3cb9b5cfbb..000000000000 --- a/apps/meteor/app/livechat/client/tabBar.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { lazy } from 'react'; - -import { addAction } from '../../../client/views/room/lib/Toolbox'; - -addAction('room-info', { - groups: ['live' /* , 'voip'*/], - id: 'room-info', - title: 'Room_Info', - icon: 'info-circled', - template: lazy(() => import('../../../client/views/omnichannel/directory/chats/contextualBar/ChatsContextualBar')), - order: 0, -}); - -addAction('voip-room-info', { - groups: ['voip'], - id: 'voip-room-info', - title: 'Call_Information', - icon: 'info-circled', - template: lazy(() => import('../../../client/views/omnichannel/directory/calls/contextualBar/CallsContextualBarRoom')), - order: 0, -}); - -addAction('contact-chat-history', { - groups: ['live' /* , 'voip'*/], - id: 'contact-chat-history', - title: 'Contact_Chat_History', - icon: 'clock', - // template: 'contactChatHistory', - template: lazy(() => import('../../../client/views/omnichannel/contactHistory/ContactHistory')), - order: 11, -}); diff --git a/apps/meteor/app/livechat/client/ui.js b/apps/meteor/app/livechat/client/ui.js index 614b410cda2e..5d8cc1696d44 100644 --- a/apps/meteor/app/livechat/client/ui.js +++ b/apps/meteor/app/livechat/client/ui.js @@ -1,14 +1,4 @@ -import { settings } from '../../settings/client'; -import { hasAllPermission } from '../../authorization/client'; -import { AccountBox, MessageTypes } from '../../ui-utils/client'; - -AccountBox.addItem({ - name: 'Omnichannel', - icon: 'headset', - href: '/omnichannel/current', - sideNav: 'omnichannelFlex', - condition: () => settings.get('Livechat_enabled') && hasAllPermission('view-livechat-manager'), -}); +import { MessageTypes } from '../../ui-utils/client'; MessageTypes.registerType({ id: 'livechat-close', diff --git a/apps/meteor/app/livechat/client/voip.ts b/apps/meteor/app/livechat/client/voip.ts index df9e7c249d54..12beb368df1d 100644 --- a/apps/meteor/app/livechat/client/voip.ts +++ b/apps/meteor/app/livechat/client/voip.ts @@ -1,6 +1,6 @@ -import moment from 'moment'; import type { IMessage } from '@rocket.chat/core-typings'; import { isVoipMessage } from '@rocket.chat/core-typings'; +import moment from 'moment'; import type { MessageType } from '../../ui-utils/client'; import { MessageTypes } from '../../ui-utils/client'; diff --git a/apps/meteor/app/livechat/imports/server/rest/appearance.ts b/apps/meteor/app/livechat/imports/server/rest/appearance.ts index f688b9da7f81..0fa365be1871 100644 --- a/apps/meteor/app/livechat/imports/server/rest/appearance.ts +++ b/apps/meteor/app/livechat/imports/server/rest/appearance.ts @@ -1,9 +1,18 @@ +import { Settings } from '@rocket.chat/models'; +import { isPOSTLivechatAppearanceParams } from '@rocket.chat/rest-typings'; + import { API } from '../../../../api/server'; import { findAppearance } from '../../../server/api/lib/appearance'; API.v1.addRoute( 'livechat/appearance', - { authRequired: true, permissionsRequired: ['view-livechat-manager'] }, + { + authRequired: true, + permissionsRequired: ['view-livechat-manager'], + validateParams: { + POST: isPOSTLivechatAppearanceParams, + }, + }, { async get() { const { appearance } = await findAppearance(); @@ -12,5 +21,44 @@ API.v1.addRoute( appearance, }); }, + async post() { + const settings = this.bodyParams; + + const validSettingList = [ + 'Livechat_title', + 'Livechat_title_color', + 'Livechat_enable_message_character_limit', + 'Livechat_message_character_limit', + 'Livechat_show_agent_info', + 'Livechat_show_agent_email', + 'Livechat_display_offline_form', + 'Livechat_offline_form_unavailable', + 'Livechat_offline_message', + 'Livechat_offline_success_message', + 'Livechat_offline_title', + 'Livechat_offline_title_color', + 'Livechat_offline_email', + 'Livechat_conversation_finished_message', + 'Livechat_conversation_finished_text', + 'Livechat_registration_form', + 'Livechat_name_field_registration_form', + 'Livechat_email_field_registration_form', + 'Livechat_registration_form_message', + ]; + + const valid = settings.every((setting) => validSettingList.includes(setting._id)); + + if (!valid) { + throw new Error('invalid-setting'); + } + + await Promise.all( + settings.map((setting) => { + return Settings.updateValueById(setting._id, setting.value); + }), + ); + + return API.v1.success(); + }, }, ); diff --git a/apps/meteor/app/livechat/imports/server/rest/dashboards.ts b/apps/meteor/app/livechat/imports/server/rest/dashboards.ts index a5dde48e8ee7..8951854bff84 100644 --- a/apps/meteor/app/livechat/imports/server/rest/dashboards.ts +++ b/apps/meteor/app/livechat/imports/server/rest/dashboards.ts @@ -1,5 +1,5 @@ -import { isGETDashboardTotalizerParams, isGETDashboardsAgentStatusParams } from '@rocket.chat/rest-typings'; import { Users } from '@rocket.chat/models'; +import { isGETDashboardTotalizerParams, isGETDashboardsAgentStatusParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; import { diff --git a/apps/meteor/app/livechat/imports/server/rest/departments.ts b/apps/meteor/app/livechat/imports/server/rest/departments.ts index 4f7886f83792..6540b67d79aa 100644 --- a/apps/meteor/app/livechat/imports/server/rest/departments.ts +++ b/apps/meteor/app/livechat/imports/server/rest/departments.ts @@ -1,11 +1,12 @@ +import type { ILivechatDepartment } from '@rocket.chat/core-typings'; +import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; import { isGETLivechatDepartmentProps, isPOSTLivechatDepartmentProps } from '@rocket.chat/rest-typings'; import { Match, check } from 'meteor/check'; -import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; -import type { ILivechatDepartment } from '@rocket.chat/core-typings'; +import { LivechatEnterprise } from '../../../../../ee/app/livechat-enterprise/server/lib/LivechatEnterprise'; import { API } from '../../../../api/server'; +import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../../../server/lib/Livechat'; import { findDepartments, findDepartmentById, @@ -14,9 +15,8 @@ import { findDepartmentAgents, findArchivedDepartments, } from '../../../server/api/lib/departments'; -import { LivechatEnterprise } from '../../../../../ee/app/livechat-enterprise/server/lib/LivechatEnterprise'; import { DepartmentHelper } from '../../../server/lib/Departments'; -import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { Livechat } from '../../../server/lib/Livechat'; API.v1.addRoute( 'livechat/department', diff --git a/apps/meteor/app/livechat/imports/server/rest/inquiries.ts b/apps/meteor/app/livechat/imports/server/rest/inquiries.ts index 26ddcacca8a2..d3a9eec7494d 100644 --- a/apps/meteor/app/livechat/imports/server/rest/inquiries.ts +++ b/apps/meteor/app/livechat/imports/server/rest/inquiries.ts @@ -1,3 +1,5 @@ +import { LivechatInquiryStatus } from '@rocket.chat/core-typings'; +import { LivechatInquiry, LivechatDepartment, Users } from '@rocket.chat/models'; import { isGETLivechatInquiriesListParams, isPOSTLivechatInquiriesTakeParams, @@ -5,12 +7,10 @@ import { isGETLivechatInquiriesQueuedForUserParams, isGETLivechatInquiriesGetOneParams, } from '@rocket.chat/rest-typings'; -import { LivechatInquiryStatus } from '@rocket.chat/core-typings'; -import { LivechatInquiry, LivechatDepartment, Users } from '@rocket.chat/models'; import { API } from '../../../../api/server'; -import { findInquiries, findOneInquiryByRoomId } from '../../../server/api/lib/inquiries'; import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { findInquiries, findOneInquiryByRoomId } from '../../../server/api/lib/inquiries'; import { takeInquiry } from '../../../server/methods/takeInquiry'; API.v1.addRoute( diff --git a/apps/meteor/app/livechat/imports/server/rest/queue.ts b/apps/meteor/app/livechat/imports/server/rest/queue.ts index 54e20ba53592..9a9f346a0ead 100644 --- a/apps/meteor/app/livechat/imports/server/rest/queue.ts +++ b/apps/meteor/app/livechat/imports/server/rest/queue.ts @@ -1,8 +1,8 @@ import { isGETLivechatQueueParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; -import { findQueueMetrics } from '../../../server/api/lib/queue'; import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { findQueueMetrics } from '../../../server/api/lib/queue'; API.v1.addRoute( 'livechat/queue', diff --git a/apps/meteor/app/livechat/imports/server/rest/rooms.ts b/apps/meteor/app/livechat/imports/server/rest/rooms.ts index eba3b52dde17..f7d5ddb314c9 100644 --- a/apps/meteor/app/livechat/imports/server/rest/rooms.ts +++ b/apps/meteor/app/livechat/imports/server/rest/rooms.ts @@ -1,10 +1,10 @@ -import { isGETLivechatRoomsParams } from '@rocket.chat/rest-typings'; import { LivechatRooms } from '@rocket.chat/models'; +import { isGETLivechatRoomsParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; -import { findRooms } from '../../../server/api/lib/rooms'; import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; +import { findRooms } from '../../../server/api/lib/rooms'; const validateDateParams = (property: string, date?: string) => { let parsedDate: { start?: string; end?: string } | undefined = undefined; diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.js b/apps/meteor/app/livechat/imports/server/rest/sms.js index e45400c0617b..6521c0f662dd 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.js +++ b/apps/meteor/app/livechat/imports/server/rest/sms.js @@ -1,15 +1,15 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; +import { OmnichannelIntegration } from '@rocket.chat/core-services'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; -import { OmnichannelIntegration } from '@rocket.chat/core-services'; +import { Random } from '@rocket.chat/random'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { Meteor } from 'meteor/meteor'; -import { FileUpload } from '../../../../file-upload/server'; import { API } from '../../../../api/server'; +import { FileUpload } from '../../../../file-upload/server'; +import { settings } from '../../../../settings/server'; import { Livechat } from '../../../server/lib/Livechat'; import { Livechat as LivechatTyped } from '../../../server/lib/LivechatTyped'; -import { settings } from '../../../../settings/server'; const getUploadFile = async (details, fileUrl) => { const response = await fetch(fileUrl); diff --git a/apps/meteor/app/livechat/imports/server/rest/triggers.ts b/apps/meteor/app/livechat/imports/server/rest/triggers.ts index 5ed544028265..a12d4a988281 100644 --- a/apps/meteor/app/livechat/imports/server/rest/triggers.ts +++ b/apps/meteor/app/livechat/imports/server/rest/triggers.ts @@ -1,12 +1,20 @@ -import { isGETLivechatTriggersParams } from '@rocket.chat/rest-typings'; +import { LivechatTrigger } from '@rocket.chat/models'; +import { isGETLivechatTriggersParams, isPOSTLivechatTriggersParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; -import { findTriggers, findTriggerById } from '../../../server/api/lib/triggers'; import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { findTriggers, findTriggerById } from '../../../server/api/lib/triggers'; API.v1.addRoute( 'livechat/triggers', - { authRequired: true, permissionsRequired: ['view-livechat-manager'], validateParams: isGETLivechatTriggersParams }, + { + authRequired: true, + permissionsRequired: ['view-livechat-manager'], + validateParams: { + GET: isGETLivechatTriggersParams, + POST: isPOSTLivechatTriggersParams, + }, + }, { async get() { const { offset, count } = await getPaginationItems(this.queryParams); @@ -22,6 +30,17 @@ API.v1.addRoute( return API.v1.success(triggers); }, + async post() { + const { _id, name, description, enabled, runOnce, conditions, actions } = this.bodyParams; + + if (_id) { + await LivechatTrigger.updateById(_id, { name, description, enabled, runOnce, conditions, actions }); + } else { + await LivechatTrigger.insertOne({ name, description, enabled, runOnce, conditions, actions }); + } + + return API.v1.success(); + }, }, ); diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.ts b/apps/meteor/app/livechat/imports/server/rest/upload.ts index f6f04ef1877f..2d4b021ffd94 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.ts +++ b/apps/meteor/app/livechat/imports/server/rest/upload.ts @@ -1,11 +1,11 @@ -import filesize from 'filesize'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; +import filesize from 'filesize'; -import { settings } from '../../../../settings/server'; -import { fileUploadIsValidContentType } from '../../../../utils/server'; -import { FileUpload } from '../../../../file-upload/server'; import { API } from '../../../../api/server'; import { getUploadFormData } from '../../../../api/server/lib/getUploadFormData'; +import { FileUpload } from '../../../../file-upload/server'; +import { settings } from '../../../../settings/server'; +import { fileUploadIsValidContentType } from '../../../../utils/server/restrictions'; import { sendFileLivechatMessage } from '../../../server/methods/sendFileLivechatMessage'; API.v1.addRoute('livechat/upload/:rid', { diff --git a/apps/meteor/app/livechat/imports/server/rest/users.ts b/apps/meteor/app/livechat/imports/server/rest/users.ts index 882a688e1a6a..680bdc7e80d2 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.ts +++ b/apps/meteor/app/livechat/imports/server/rest/users.ts @@ -1,13 +1,13 @@ +import { Users } from '@rocket.chat/models'; +import { isLivechatUsersManagerGETProps, isPOSTLivechatUsersTypeProps } from '@rocket.chat/rest-typings'; import { check } from 'meteor/check'; import _ from 'underscore'; -import { isLivechatUsersManagerGETProps, isPOSTLivechatUsersTypeProps } from '@rocket.chat/rest-typings'; -import { Users } from '@rocket.chat/models'; import { API } from '../../../../api/server'; -import { Livechat } from '../../../server/lib/Livechat'; -import { findAgents, findManagers } from '../../../server/api/lib/users'; -import { hasAtLeastOnePermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { hasAtLeastOnePermissionAsync } from '../../../../authorization/server/functions/hasPermission'; +import { findAgents, findManagers } from '../../../server/api/lib/users'; +import { Livechat } from '../../../server/lib/Livechat'; API.v1.addRoute( 'livechat/users/:type', diff --git a/apps/meteor/app/livechat/imports/server/rest/visitors.ts b/apps/meteor/app/livechat/imports/server/rest/visitors.ts index 09ca9dd4d59c..5504d85b699f 100644 --- a/apps/meteor/app/livechat/imports/server/rest/visitors.ts +++ b/apps/meteor/app/livechat/imports/server/rest/visitors.ts @@ -1,4 +1,4 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Messages, LivechatRooms } from '@rocket.chat/models'; import { isLivechatVisitorsInfoProps, isGETLivechatVisitorsPagesVisitedRoomIdParams, @@ -8,9 +8,12 @@ import { isLivechatRidMessagesProps, isGETLivechatVisitorsSearch, } from '@rocket.chat/rest-typings'; -import { Messages, LivechatRooms } from '@rocket.chat/models'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; import { API } from '../../../../api/server'; +import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { canAccessRoomAsync } from '../../../../authorization/server'; +import { normalizeMessagesForUser } from '../../../../utils/server/lib/normalizeMessagesForUser'; import { findVisitorInfo, findVisitedPages, @@ -19,9 +22,6 @@ import { findVisitorsToAutocomplete, findVisitorsByEmailOrPhoneOrNameOrUsernameOrCustomField, } from '../../../server/api/lib/visitors'; -import { normalizeMessagesForUser } from '../../../../utils/server/lib/normalizeMessagesForUser'; -import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; -import { canAccessRoomAsync } from '../../../../authorization/server'; API.v1.addRoute( 'livechat/visitors.info', diff --git a/apps/meteor/app/livechat/lib/inquiries.ts b/apps/meteor/app/livechat/lib/inquiries.ts index dddf32ee5467..488151aa4166 100644 --- a/apps/meteor/app/livechat/lib/inquiries.ts +++ b/apps/meteor/app/livechat/lib/inquiries.ts @@ -6,13 +6,16 @@ type ReturnType = | { priorityWeight: SortOrder; ts: SortOrder; + _updatedAt: SortOrder; } | { estimatedWaitingTimeQueue: SortOrder; ts: SortOrder; + _updatedAt: SortOrder; } | { ts: SortOrder; + _updatedAt: SortOrder; }; export const getOmniChatSortQuery = ( @@ -20,11 +23,11 @@ export const getOmniChatSortQuery = ( ): ReturnType => { switch (sortByMechanism) { case OmnichannelSortingMechanismSettingType.Priority: - return { priorityWeight: 1, ts: 1 }; + return { priorityWeight: 1, ts: 1, _updatedAt: -1 }; case OmnichannelSortingMechanismSettingType.SLAs: - return { estimatedWaitingTimeQueue: 1, ts: 1 }; + return { estimatedWaitingTimeQueue: 1, ts: 1, _updatedAt: -1 }; case OmnichannelSortingMechanismSettingType.Timestamp: default: - return { ts: 1 }; + return { ts: 1, _updatedAt: -1 }; } }; diff --git a/apps/meteor/app/livechat/lib/messageTypes.ts b/apps/meteor/app/livechat/lib/messageTypes.ts index bccc8a73fab4..ace0d4e65bfa 100644 --- a/apps/meteor/app/livechat/lib/messageTypes.ts +++ b/apps/meteor/app/livechat/lib/messageTypes.ts @@ -1,7 +1,7 @@ +import type { IOmnichannelSystemMessage } from '@rocket.chat/core-typings'; +import { escapeHTML } from '@rocket.chat/string-helpers'; import formatDistance from 'date-fns/formatDistance'; import moment from 'moment'; -import { escapeHTML } from '@rocket.chat/string-helpers'; -import type { IOmnichannelSystemMessage } from '@rocket.chat/core-typings'; import { MessageTypes } from '../../ui-utils/lib/MessageTypes'; import { t } from '../../utils/lib/i18n'; diff --git a/apps/meteor/app/livechat/server/api/lib/customFields.ts b/apps/meteor/app/livechat/server/api/lib/customFields.ts index e614b6dc1b78..0a9213f99f99 100644 --- a/apps/meteor/app/livechat/server/api/lib/customFields.ts +++ b/apps/meteor/app/livechat/server/api/lib/customFields.ts @@ -1,7 +1,7 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; +import type { ILivechatCustomField } from '@rocket.chat/core-typings'; import { LivechatCustomField } from '@rocket.chat/models'; import type { PaginatedResult } from '@rocket.chat/rest-typings'; -import type { ILivechatCustomField } from '@rocket.chat/core-typings'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; export async function findLivechatCustomFields({ text, diff --git a/apps/meteor/app/livechat/server/api/lib/departments.ts b/apps/meteor/app/livechat/server/api/lib/departments.ts index 6d9ebb9c8c25..049dbebaf7aa 100644 --- a/apps/meteor/app/livechat/server/api/lib/departments.ts +++ b/apps/meteor/app/livechat/server/api/lib/departments.ts @@ -1,11 +1,11 @@ -import type { Document, Filter, FindOptions } from 'mongodb'; -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import type { PaginatedResult } from '@rocket.chat/rest-typings'; import type { ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; +import type { PaginatedResult } from '@rocket.chat/rest-typings'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; +import type { Document, Filter, FindOptions } from 'mongodb'; -import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { callbacks } from '../../../../../lib/callbacks'; +import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; type Pagination = { pagination: { offset: number; count: number; sort: FindOptions['sort'] } }; type FindDepartmentParams = { @@ -129,7 +129,7 @@ export async function findDepartmentById({ department: await LivechatDepartment.findOne(query), ...(includeAgents && canViewLivechatDepartments && { - agents: await LivechatDepartmentAgents.find({ departmentId }).toArray(), + agents: await LivechatDepartmentAgents.findByDepartmentId(departmentId).toArray(), }), }; @@ -192,6 +192,6 @@ export async function findDepartmentsBetweenIds({ ids: string[]; fields: Record; }): Promise<{ departments: ILivechatDepartment[] }> { - const departments = await LivechatDepartment.findInIds(ids, fields).toArray(); + const departments = await LivechatDepartment.findInIds(ids, { projection: fields }).toArray(); return { departments }; } diff --git a/apps/meteor/app/livechat/server/api/lib/inquiries.ts b/apps/meteor/app/livechat/server/api/lib/inquiries.ts index 450e035af257..19cbfc21ede9 100644 --- a/apps/meteor/app/livechat/server/api/lib/inquiries.ts +++ b/apps/meteor/app/livechat/server/api/lib/inquiries.ts @@ -8,8 +8,10 @@ import { getOmniChatSortQuery } from '../../../lib/inquiries'; import { getInquirySortMechanismSetting } from '../../lib/settings'; const agentDepartments = async (userId: IUser['_id']): Promise => { - const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId).toArray()).map(({ departmentId }) => departmentId); - return (await LivechatDepartment.find({ _id: { $in: agentDepartments }, enabled: true }).toArray()).map(({ _id }) => _id); + const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId, { projection: { departmentId: 1 } }).toArray()).map( + ({ departmentId }) => departmentId, + ); + return (await LivechatDepartment.findEnabledInIds(agentDepartments, { projection: { _id: 1 } }).toArray()).map(({ _id }) => _id); }; const applyDepartmentRestrictions = async ( diff --git a/apps/meteor/app/livechat/server/api/lib/livechat.ts b/apps/meteor/app/livechat/server/api/lib/livechat.ts index 8f2719f88551..7bb608090557 100644 --- a/apps/meteor/app/livechat/server/api/lib/livechat.ts +++ b/apps/meteor/app/livechat/server/api/lib/livechat.ts @@ -1,6 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; -import { EmojiCustom, LivechatTrigger, LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; import type { ILivechatAgent, ILivechatDepartment, @@ -9,12 +6,15 @@ import type { IOmnichannelRoom, SelectedAgent, } from '@rocket.chat/core-typings'; +import { EmojiCustom, LivechatTrigger, LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../../lib/Livechat'; -import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; import { callbacks } from '../../../../../lib/callbacks'; -import { normalizeAgent } from '../../lib/Helper'; import { i18n } from '../../../../../server/lib/i18n'; +import { normalizeAgent } from '../../lib/Helper'; +import { Livechat } from '../../lib/Livechat'; +import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; export function online(department: string, skipSettingCheck = false, skipFallbackCheck = false): Promise { return LivechatTyped.online(department, skipSettingCheck, skipFallbackCheck); @@ -126,7 +126,7 @@ export function getRoom({ return LivechatTyped.getRoom(guest, message, roomInfo, agent, extraParams); } -export async function findAgent(agentId?: string): Promise { +export async function findAgent(agentId?: string): Promise { return normalizeAgent(agentId); } diff --git a/apps/meteor/app/livechat/server/api/lib/users.ts b/apps/meteor/app/livechat/server/api/lib/users.ts index 72833d67cea8..948cde0f8bfd 100644 --- a/apps/meteor/app/livechat/server/api/lib/users.ts +++ b/apps/meteor/app/livechat/server/api/lib/users.ts @@ -1,6 +1,6 @@ -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { Users } from '@rocket.chat/models'; import type { ILivechatAgent, IRole } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; /** * @param {IRole['_id']} role the role id @@ -24,7 +24,12 @@ async function findUsers({ }); } - const { cursor, totalCount } = Users.findPaginatedUsersInRolesWithQuery(role, query, { + const [ + { + sortedResults, + totalCount: [{ total } = { total: 0 }], + }, + ] = await Users.findAgentsWithDepartments(role, query, { sort: sort || { name: 1 }, skip: offset, limit: count, @@ -38,11 +43,9 @@ async function findUsers({ }, }); - const [users, total] = await Promise.all([cursor.toArray(), totalCount]); - return { - users, - count: users.length, + users: sortedResults, + count: sortedResults.length, offset, total, }; diff --git a/apps/meteor/app/livechat/server/api/lib/visitors.ts b/apps/meteor/app/livechat/server/api/lib/visitors.ts index 2c989bd872aa..e559aecc892e 100644 --- a/apps/meteor/app/livechat/server/api/lib/visitors.ts +++ b/apps/meteor/app/livechat/server/api/lib/visitors.ts @@ -2,8 +2,8 @@ import type { ILivechatVisitor, IMessage, IOmnichannelRoom, IRoom, IUser, IVisit import { LivechatVisitors, Messages, LivechatRooms, LivechatCustomField } from '@rocket.chat/models'; import type { FindOptions } from 'mongodb'; -import { canAccessRoomAsync } from '../../../../authorization/server/functions/canAccessRoom'; import { callbacks } from '../../../../../lib/callbacks'; +import { canAccessRoomAsync } from '../../../../authorization/server/functions/canAccessRoom'; export async function findVisitorInfo({ visitorId }: { visitorId: IVisitor['_id'] }) { const visitor = await LivechatVisitors.findOneById(visitorId); diff --git a/apps/meteor/app/livechat/server/api/rest.ts b/apps/meteor/app/livechat/server/api/rest.ts index d1eb008ab4df..f9da6690185e 100644 --- a/apps/meteor/app/livechat/server/api/rest.ts +++ b/apps/meteor/app/livechat/server/api/rest.ts @@ -12,3 +12,4 @@ import './v1/transfer'; import './v1/contact'; import './v1/webhooks'; import './v1/integration'; +import './v1/statistics'; diff --git a/apps/meteor/app/livechat/server/api/v1/agent.ts b/apps/meteor/app/livechat/server/api/v1/agent.ts index 370e4d66ef20..7a7b70ea7c8b 100644 --- a/apps/meteor/app/livechat/server/api/v1/agent.ts +++ b/apps/meteor/app/livechat/server/api/v1/agent.ts @@ -1,13 +1,13 @@ -import { isGETAgentNextToken, isPOSTLivechatAgentStatusProps } from '@rocket.chat/rest-typings'; -import { Users } from '@rocket.chat/models'; import type { ILivechatAgent } from '@rocket.chat/core-typings'; import { ILivechatAgentStatus } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { isGETAgentNextToken, isPOSTLivechatAgentStatusProps } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; -import { findRoom, findGuest, findAgent, findOpenRoom } from '../lib/livechat'; +import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { Livechat } from '../../lib/Livechat'; import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; -import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; +import { findRoom, findGuest, findAgent, findOpenRoom } from '../lib/livechat'; API.v1.addRoute('livechat/agent.info/:rid/:token', { async get() { @@ -73,16 +73,21 @@ API.v1.addRoute( const agentId = inputAgentId || this.userId; - const agent = await Users.findOneAgentById>(agentId, { + const agent = await Users.findOneAgentById>(agentId, { projection: { status: 1, statusLivechat: 1, + active: 1, }, }); if (!agent) { return API.v1.notFound('Agent not found'); } + if (!agent.active) { + return API.v1.failure('error-user-deactivated'); + } + const newStatus: ILivechatAgentStatus = status || (agent.statusLivechat === ILivechatAgentStatus.AVAILABLE ? ILivechatAgentStatus.NOT_AVAILABLE : ILivechatAgentStatus.AVAILABLE); diff --git a/apps/meteor/app/livechat/server/api/v1/config.ts b/apps/meteor/app/livechat/server/api/v1/config.ts index f2180969161c..37264721cb49 100644 --- a/apps/meteor/app/livechat/server/api/v1/config.ts +++ b/apps/meteor/app/livechat/server/api/v1/config.ts @@ -1,5 +1,5 @@ -import mem from 'mem'; import { isGETLivechatConfigParams } from '@rocket.chat/rest-typings'; +import mem from 'mem'; import { API } from '../../../../api/server'; import { Livechat } from '../../lib/LivechatTyped'; diff --git a/apps/meteor/app/livechat/server/api/v1/contact.ts b/apps/meteor/app/livechat/server/api/v1/contact.ts index 9d6f9b959117..517acf33f137 100644 --- a/apps/meteor/app/livechat/server/api/v1/contact.ts +++ b/apps/meteor/app/livechat/server/api/v1/contact.ts @@ -1,7 +1,7 @@ -import { Match, check } from 'meteor/check'; -import { Meteor } from 'meteor/meteor'; import { LivechatCustomField, LivechatVisitors } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { API } from '../../../../api/server'; import { Contacts } from '../../lib/Contacts'; diff --git a/apps/meteor/app/livechat/server/api/v1/customField.ts b/apps/meteor/app/livechat/server/api/v1/customField.ts index 7c25aeccc37a..15152d2143ff 100644 --- a/apps/meteor/app/livechat/server/api/v1/customField.ts +++ b/apps/meteor/app/livechat/server/api/v1/customField.ts @@ -1,10 +1,10 @@ import { isLivechatCustomFieldsProps, isPOSTLivechatCustomFieldParams, isPOSTLivechatCustomFieldsParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; -import { findGuest } from '../lib/livechat'; +import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; import { Livechat } from '../../lib/Livechat'; import { findLivechatCustomFields, findCustomFieldById } from '../lib/customFields'; -import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { findGuest } from '../lib/livechat'; API.v1.addRoute( 'livechat/custom.field', diff --git a/apps/meteor/app/livechat/server/api/v1/integration.ts b/apps/meteor/app/livechat/server/api/v1/integration.ts index 7226bee660f8..6cf8ffd52192 100644 --- a/apps/meteor/app/livechat/server/api/v1/integration.ts +++ b/apps/meteor/app/livechat/server/api/v1/integration.ts @@ -1,8 +1,8 @@ import { Settings } from '@rocket.chat/models'; import { isPOSTomnichannelIntegrations } from '@rocket.chat/rest-typings'; -import { API } from '../../../../api/server'; import { trim } from '../../../../../lib/utils/stringUtils'; +import { API } from '../../../../api/server'; API.v1.addRoute( 'omnichannel/integrations', diff --git a/apps/meteor/app/livechat/server/api/v1/message.ts b/apps/meteor/app/livechat/server/api/v1/message.ts index a349d890a4e1..2b6f4c00af53 100644 --- a/apps/meteor/app/livechat/server/api/v1/message.ts +++ b/apps/meteor/app/livechat/server/api/v1/message.ts @@ -1,5 +1,6 @@ -import { Random } from '@rocket.chat/random'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors, LivechatRooms, Messages } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; import { isPOSTLivechatMessageParams, isGETLivechatMessageIdParams, @@ -8,18 +9,17 @@ import { isGETLivechatMessagesHistoryRidParams, isGETLivechatMessagesParams, } from '@rocket.chat/rest-typings'; -import { LivechatVisitors, LivechatRooms, Messages } from '@rocket.chat/models'; +import { callbacks } from '../../../../../lib/callbacks'; import { API } from '../../../../api/server'; +import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { isWidget } from '../../../../api/server/helpers/isWidget'; import { loadMessageHistory } from '../../../../lib/server/functions/loadMessageHistory'; -import { findGuest, findRoom, normalizeHttpHeaderData } from '../lib/livechat'; +import { settings } from '../../../../settings/server'; +import { normalizeMessageFileUpload } from '../../../../utils/server/functions/normalizeMessageFileUpload'; import { Livechat } from '../../lib/Livechat'; import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; -import { normalizeMessageFileUpload } from '../../../../utils/server/functions/normalizeMessageFileUpload'; -import { settings } from '../../../../settings/server'; -import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; -import { isWidget } from '../../../../api/server/helpers/isWidget'; -import { callbacks } from '../../../../../lib/callbacks'; +import { findGuest, findRoom, normalizeHttpHeaderData } from '../lib/livechat'; API.v1.addRoute( 'livechat/message', diff --git a/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts b/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts index 52dda88b0c12..b01e60d2265f 100644 --- a/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts +++ b/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts @@ -1,8 +1,8 @@ import { isPOSTLivechatOfflineMessageParams } from '@rocket.chat/rest-typings'; +import { i18n } from '../../../../../server/lib/i18n'; import { API } from '../../../../api/server'; import { Livechat } from '../../lib/Livechat'; -import { i18n } from '../../../../../server/lib/i18n'; API.v1.addRoute( 'livechat/offline.message', @@ -10,11 +10,12 @@ API.v1.addRoute( { async post() { const { name, email, message, department, host } = this.bodyParams; - if (!(await Livechat.sendOfflineMessage({ name, email, message, department, host }))) { - return API.v1.failure({ message: i18n.t('Error_sending_livechat_offline_message') }); + try { + await Livechat.sendOfflineMessage({ name, email, message, department, host }); + return API.v1.success({ message: i18n.t('Livechat_offline_message_sent') }); + } catch (e) { + return API.v1.failure(i18n.t('Error_sending_livechat_offline_message')); } - - return API.v1.success({ message: i18n.t('Livechat_offline_message_sent') }); }, }, ); diff --git a/apps/meteor/app/livechat/server/api/v1/pageVisited.ts b/apps/meteor/app/livechat/server/api/v1/pageVisited.ts index 39a63e117473..9e9434bffe93 100644 --- a/apps/meteor/app/livechat/server/api/v1/pageVisited.ts +++ b/apps/meteor/app/livechat/server/api/v1/pageVisited.ts @@ -1,6 +1,6 @@ -import { isPOSTLivechatPageVisitedParams } from '@rocket.chat/rest-typings'; -import { Messages } from '@rocket.chat/models'; import type { IOmnichannelSystemMessage } from '@rocket.chat/core-typings'; +import { Messages } from '@rocket.chat/models'; +import { isPOSTLivechatPageVisitedParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; import { Livechat } from '../../lib/Livechat'; diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index d43067747d62..0fe60248bfba 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -1,9 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { Random } from '@rocket.chat/random'; -import type { ILivechatAgent, IOmnichannelRoom, IUser, SelectedAgent } from '@rocket.chat/core-typings'; +import type { ILivechatAgent, IOmnichannelRoom, IUser, SelectedAgent, TransferByData } from '@rocket.chat/core-typings'; import { isOmnichannelRoom, OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, Users, LivechatRooms, Subscriptions, Messages } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; import { isLiveChatRoomForwardProps, isPOSTLivechatRoomCloseParams, @@ -14,23 +12,24 @@ import { isLiveChatRoomSaveInfoProps, isPOSTLivechatRoomCloseByUserParams, } from '@rocket.chat/rest-typings'; +import { check } from 'meteor/check'; -import { settings as rcSettings } from '../../../../settings/server'; +import { callbacks } from '../../../../../lib/callbacks'; +import { i18n } from '../../../../../server/lib/i18n'; import { API } from '../../../../api/server'; -import { findGuest, findRoom, getRoom, settings, findAgent, onCheckRoomParams } from '../lib/livechat'; -import { Livechat } from '../../lib/Livechat'; -import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; -import { normalizeTransferredByData } from '../../lib/Helper'; -import { findVisitorInfo } from '../lib/visitors'; +import { isWidget } from '../../../../api/server/helpers/isWidget'; import { canAccessRoomAsync } from '../../../../authorization/server'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { addUserToRoom } from '../../../../lib/server/functions'; -import { callbacks } from '../../../../../lib/callbacks'; +import { addUserToRoom } from '../../../../lib/server/functions/addUserToRoom'; +import { settings as rcSettings } from '../../../../settings/server'; +import { normalizeTransferredByData } from '../../lib/Helper'; +import { Livechat } from '../../lib/Livechat'; import type { CloseRoomParams } from '../../lib/LivechatTyped'; -import { isWidget } from '../../../../api/server/helpers/isWidget'; -import { i18n } from '../../../../../server/lib/i18n'; +import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; +import { findGuest, findRoom, getRoom, settings, findAgent, onCheckRoomParams } from '../lib/livechat'; +import { findVisitorInfo } from '../lib/visitors'; -const isAgentWithInfo = (agentObj: ILivechatAgent | { hiddenInfo: true }): agentObj is ILivechatAgent => !('hiddenInfo' in agentObj); +const isAgentWithInfo = (agentObj: ILivechatAgent | { hiddenInfo: boolean }): agentObj is ILivechatAgent => !('hiddenInfo' in agentObj); API.v1.addRoute('livechat/room', { async get() { @@ -328,7 +327,8 @@ API.v1.addRoute( } const guest = await LivechatVisitors.findOneById(room.v?._id); - transferData.transferredBy = normalizeTransferredByData((await Meteor.userAsync()) || {}, room); + const transferedBy = this.user satisfies TransferByData; + transferData.transferredBy = normalizeTransferredByData(transferedBy, room); if (transferData.userId) { const userToTransfer = await Users.findOneById(transferData.userId); if (userToTransfer) { diff --git a/apps/meteor/app/livechat/server/api/v1/statistics.ts b/apps/meteor/app/livechat/server/api/v1/statistics.ts new file mode 100644 index 000000000000..078f366bb484 --- /dev/null +++ b/apps/meteor/app/livechat/server/api/v1/statistics.ts @@ -0,0 +1,65 @@ +import { Users } from '@rocket.chat/models'; +import { isLivechatAnalyticsAgentOverviewProps, isLivechatAnalyticsOverviewProps } from '@rocket.chat/rest-typings'; + +import { API } from '../../../../api/server'; +import { settings } from '../../../../settings/server'; +import { Livechat } from '../../lib/Livechat'; + +API.v1.addRoute( + 'livechat/analytics/agent-overview', + { + authRequired: true, + permissionsRequired: ['view-livechat-manager'], + validateParams: isLivechatAnalyticsAgentOverviewProps, + }, + { + async get() { + const { name, departmentId, from, to } = this.queryParams; + + if (!name) { + throw new Error('invalid-chart-name'); + } + + const user = await Users.findOneById(this.userId, { projection: { _id: 1, utcOffset: 1 } }); + return API.v1.success( + await Livechat.Analytics.getAgentOverviewData({ + departmentId, + utcOffset: user?.utcOffset || 0, + daterange: { from, to }, + chartOptions: { name }, + }), + ); + }, + }, +); + +API.v1.addRoute( + 'livechat/analytics/overview', + { + authRequired: true, + permissionsRequired: ['view-livechat-manager'], + validateParams: isLivechatAnalyticsOverviewProps, + }, + { + async get() { + const { name, departmentId, from, to } = this.queryParams; + + if (!name) { + throw new Error('invalid-chart-name'); + } + + const user = await Users.findOneById(this.userId, { projection: { _id: 1, utcOffset: 1 } }); + const language = user?.language || settings.get('Language') || 'en'; + + return API.v1.success( + await Livechat.Analytics.getAnalyticsOverviewData({ + departmentId, + utcOffset: user?.utcOffset || 0, + daterange: { from, to }, + analyticsOptions: { name }, + language, + }), + ); + }, + }, +); diff --git a/apps/meteor/app/livechat/server/api/v1/transcript.ts b/apps/meteor/app/livechat/server/api/v1/transcript.ts index c10a63e98e06..3eaa91c37c7c 100644 --- a/apps/meteor/app/livechat/server/api/v1/transcript.ts +++ b/apps/meteor/app/livechat/server/api/v1/transcript.ts @@ -1,8 +1,11 @@ -import { isPOSTLivechatTranscriptParams } from '@rocket.chat/rest-typings'; +import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatRooms, Users } from '@rocket.chat/models'; +import { isPOSTLivechatTranscriptParams, isPOSTLivechatTranscriptRequestParams } from '@rocket.chat/rest-typings'; +import { i18n } from '../../../../../server/lib/i18n'; import { API } from '../../../../api/server'; +import { Livechat as LivechatJS } from '../../lib/Livechat'; import { Livechat } from '../../lib/LivechatTyped'; -import { i18n } from '../../../../../server/lib/i18n'; API.v1.addRoute( 'livechat/transcript', @@ -18,3 +21,45 @@ API.v1.addRoute( }, }, ); + +API.v1.addRoute( + 'livechat/transcript/:rid', + { + authRequired: true, + permissionsRequired: ['send-omnichannel-chat-transcript'], + validateParams: { + POST: isPOSTLivechatTranscriptRequestParams, + }, + }, + { + async delete() { + const { rid } = this.urlParams; + const room = await LivechatRooms.findOneById>(rid, { + projection: { open: 1, transcriptRequest: 1 }, + }); + + if (!room?.open) { + throw new Error('error-invalid-room'); + } + if (!room.transcriptRequest) { + throw new Error('error-transcript-not-requested'); + } + + await LivechatRooms.unsetEmailTranscriptRequestedByRoomId(rid); + + return API.v1.success(); + }, + async post() { + const { rid } = this.urlParams; + const { email, subject } = this.bodyParams; + + const user = await Users.findOneById(this.userId, { + projection: { _id: 1, username: 1, name: 1, utcOffset: 1 }, + }); + + await LivechatJS.requestTranscript({ rid, email, subject, user }); + + return API.v1.success(); + }, + }, +); diff --git a/apps/meteor/app/livechat/server/api/v1/transfer.ts b/apps/meteor/app/livechat/server/api/v1/transfer.ts index 0ec743b23168..8d1b33fdcc3d 100644 --- a/apps/meteor/app/livechat/server/api/v1/transfer.ts +++ b/apps/meteor/app/livechat/server/api/v1/transfer.ts @@ -1,8 +1,8 @@ import { LivechatRooms } from '@rocket.chat/models'; import { API } from '../../../../api/server'; -import { findLivechatTransferHistory } from '../lib/transfer'; import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems'; +import { findLivechatTransferHistory } from '../lib/transfer'; API.v1.addRoute( 'livechat/transfer.history/:rid', diff --git a/apps/meteor/app/livechat/server/api/v1/videoCall.ts b/apps/meteor/app/livechat/server/api/v1/videoCall.ts index 3c42196f7546..5ce0ddc4ca37 100644 --- a/apps/meteor/app/livechat/server/api/v1/videoCall.ts +++ b/apps/meteor/app/livechat/server/api/v1/videoCall.ts @@ -1,13 +1,13 @@ -import { isGETWebRTCCall, isPUTWebRTCCallId } from '@rocket.chat/rest-typings'; -import { Messages, Settings, Rooms } from '@rocket.chat/models'; import { Message } from '@rocket.chat/core-services'; +import { Messages, Settings, Rooms } from '@rocket.chat/models'; +import { isGETWebRTCCall, isPUTWebRTCCallId } from '@rocket.chat/rest-typings'; -import { settings as rcSettings } from '../../../../settings/server'; +import { i18n } from '../../../../../server/lib/i18n'; import { API } from '../../../../api/server'; -import { settings } from '../lib/livechat'; import { canSendMessageAsync } from '../../../../authorization/server/functions/canSendMessage'; +import { settings as rcSettings } from '../../../../settings/server'; import { Livechat } from '../../lib/Livechat'; -import { i18n } from '../../../../../server/lib/i18n'; +import { settings } from '../lib/livechat'; API.v1.addRoute( 'livechat/webrtc.call', diff --git a/apps/meteor/app/livechat/server/api/v1/visitor.ts b/apps/meteor/app/livechat/server/api/v1/visitor.ts index 1d1291b9493e..012b412639ea 100644 --- a/apps/meteor/app/livechat/server/api/v1/visitor.ts +++ b/apps/meteor/app/livechat/server/api/v1/visitor.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import type { IRoom } from '@rocket.chat/core-typings'; import { LivechatVisitors as VisitorsRaw, LivechatCustomField, LivechatRooms } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { callbacks } from '../../../../../lib/callbacks'; import { API } from '../../../../api/server'; -import { findGuest, normalizeHttpHeaderData } from '../lib/livechat'; +import { settings } from '../../../../settings/server'; import { Livechat } from '../../lib/Livechat'; import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; -import { settings } from '../../../../settings/server'; -import { callbacks } from '../../../../../lib/callbacks'; +import { findGuest, normalizeHttpHeaderData } from '../lib/livechat'; API.v1.addRoute('livechat/visitor', { async post() { diff --git a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts index 7f9e8eaa404b..55de5bbf6315 100644 --- a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts +++ b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts @@ -1,7 +1,7 @@ -import moment from 'moment-timezone'; import type { ILivechatAgentStatus, ILivechatBusinessHour, ILivechatDepartment } from '@rocket.chat/core-typings'; import type { ILivechatBusinessHoursModel, IUsersModel } from '@rocket.chat/model-typings'; import { LivechatBusinessHours, Users } from '@rocket.chat/models'; +import moment from 'moment-timezone'; import type { UpdateFilter } from 'mongodb'; import type { IWorkHoursCronJobsWrapper } from '../../../../server/models/raw/LivechatBusinessHours'; diff --git a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts index 6b14f82856a0..52ccd0441e24 100644 --- a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts +++ b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts @@ -1,14 +1,14 @@ -import moment from 'moment'; import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import type { AgendaCronJobs } from '@rocket.chat/cron'; import { LivechatDepartment, Users } from '@rocket.chat/models'; +import moment from 'moment'; -import type { IBusinessHourBehavior, IBusinessHourType } from './AbstractBusinessHour'; -import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; import { closeBusinessHour } from '../../../../ee/app/livechat-enterprise/server/business-hour/Helper'; +import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; import { businessHourLogger } from '../lib/logger'; +import type { IBusinessHourBehavior, IBusinessHourType } from './AbstractBusinessHour'; export class BusinessHourManager { private types: Map = new Map(); diff --git a/apps/meteor/app/livechat/server/business-hour/Default.ts b/apps/meteor/app/livechat/server/business-hour/Default.ts index b62a687479a1..e14567d9072d 100644 --- a/apps/meteor/app/livechat/server/business-hour/Default.ts +++ b/apps/meteor/app/livechat/server/business-hour/Default.ts @@ -1,6 +1,6 @@ -import moment from 'moment-timezone'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; +import moment from 'moment-timezone'; import type { IBusinessHourType } from './AbstractBusinessHour'; import { AbstractBusinessHourType } from './AbstractBusinessHour'; diff --git a/apps/meteor/app/livechat/server/business-hour/Helper.ts b/apps/meteor/app/livechat/server/business-hour/Helper.ts index 2106a962e4f7..e61bb1621765 100644 --- a/apps/meteor/app/livechat/server/business-hour/Helper.ts +++ b/apps/meteor/app/livechat/server/business-hour/Helper.ts @@ -1,10 +1,10 @@ -import moment from 'moment'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import { LivechatBusinessHours, Users } from '@rocket.chat/models'; +import moment from 'moment'; -import { createDefaultBusinessHourRow } from './LivechatBusinessHours'; import { businessHourLogger } from '../lib/logger'; +import { createDefaultBusinessHourRow } from './LivechatBusinessHours'; export const filterBusinessHoursThatMustBeOpened = async ( businessHours: ILivechatBusinessHour[], diff --git a/apps/meteor/app/livechat/server/business-hour/LivechatBusinessHours.ts b/apps/meteor/app/livechat/server/business-hour/LivechatBusinessHours.ts index d0b8e0069ce2..92f3ed840682 100644 --- a/apps/meteor/app/livechat/server/business-hour/LivechatBusinessHours.ts +++ b/apps/meteor/app/livechat/server/business-hour/LivechatBusinessHours.ts @@ -1,7 +1,7 @@ -import moment from 'moment-timezone'; -import { ObjectId } from 'mongodb'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; +import moment from 'moment-timezone'; +import { ObjectId } from 'mongodb'; export const createDefaultBusinessHourRow = (): ILivechatBusinessHour => { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; diff --git a/apps/meteor/app/livechat/server/business-hour/Single.ts b/apps/meteor/app/livechat/server/business-hour/Single.ts index cdb02d59d2b9..d899f2717376 100644 --- a/apps/meteor/app/livechat/server/business-hour/Single.ts +++ b/apps/meteor/app/livechat/server/business-hour/Single.ts @@ -1,10 +1,10 @@ import { ILivechatAgentStatus, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import { LivechatBusinessHours, Users } from '@rocket.chat/models'; +import { businessHourLogger } from '../lib/logger'; import type { IBusinessHourBehavior } from './AbstractBusinessHour'; import { AbstractBusinessHourBehavior } from './AbstractBusinessHour'; import { filterBusinessHoursThatMustBeOpened, openBusinessHourDefault } from './Helper'; -import { businessHourLogger } from '../lib/logger'; export class SingleBusinessHourBehavior extends AbstractBusinessHourBehavior implements IBusinessHourBehavior { async openBusinessHoursByDayAndHour(): Promise { diff --git a/apps/meteor/app/livechat/server/business-hour/index.ts b/apps/meteor/app/livechat/server/business-hour/index.ts index 36c67bf74d66..8b5f10b938ff 100644 --- a/apps/meteor/app/livechat/server/business-hour/index.ts +++ b/apps/meteor/app/livechat/server/business-hour/index.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { cronJobs } from '@rocket.chat/cron'; import type { IUser } from '@rocket.chat/core-typings'; +import { cronJobs } from '@rocket.chat/cron'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { BusinessHourManager } from './BusinessHourManager'; -import { SingleBusinessHourBehavior } from './Single'; import { callbacks } from '../../../../lib/callbacks'; +import { BusinessHourManager } from './BusinessHourManager'; import { DefaultBusinessHour } from './Default'; +import { SingleBusinessHourBehavior } from './Single'; export const businessHourManager = new BusinessHourManager(cronJobs); diff --git a/apps/meteor/app/livechat/server/hooks/afterAgentRemoved.ts b/apps/meteor/app/livechat/server/hooks/afterAgentRemoved.ts new file mode 100644 index 000000000000..475eff9002fc --- /dev/null +++ b/apps/meteor/app/livechat/server/hooks/afterAgentRemoved.ts @@ -0,0 +1,13 @@ +import { LivechatDepartment, Users, LivechatDepartmentAgents, LivechatVisitors } from '@rocket.chat/models'; + +import { callbacks } from '../../../../lib/callbacks'; + +callbacks.add('livechat.afterAgentRemoved', async ({ agent }) => { + const departmentIds = (await LivechatDepartmentAgents.findByAgentId(agent._id).toArray()).map((department) => department.departmentId); + await Promise.all([ + Users.removeAgent(agent._id), + LivechatDepartmentAgents.removeByAgentId(agent._id), + agent.username && LivechatVisitors.removeContactManagerByUsername(agent.username), + departmentIds.length && LivechatDepartment.decreaseNumberOfAgentsByIds(departmentIds), + ]); +}); diff --git a/apps/meteor/app/livechat/server/hooks/leadCapture.ts b/apps/meteor/app/livechat/server/hooks/leadCapture.ts index 74d3a677d110..4b987c00c02e 100644 --- a/apps/meteor/app/livechat/server/hooks/leadCapture.ts +++ b/apps/meteor/app/livechat/server/hooks/leadCapture.ts @@ -1,10 +1,10 @@ -import { LivechatVisitors } from '@rocket.chat/models'; import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings/server'; import { isTruthy } from '../../../../lib/isTruthy'; +import { settings } from '../../../settings/server'; function validateMessage(message: IMessage, room: IOmnichannelRoom) { // skips this callback if the message was edited @@ -32,7 +32,7 @@ function validateMessage(message: IMessage, room: IOmnichannelRoom) { callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { if (!isOmnichannelRoom(room)) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts index 9d499aa9ed9b..f0bfb8574e6a 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts +++ b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts @@ -5,7 +5,7 @@ import { callbacks } from '../../../../lib/callbacks'; callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { if (!isOmnichannelRoom(room)) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts b/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts index ed7ab25e42b3..5ebf924e7334 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts +++ b/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts @@ -1,3 +1,4 @@ +import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; @@ -5,7 +6,7 @@ import { callbacks } from '../../../../lib/callbacks'; callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { if (!isOmnichannelRoom(room)) { return message; } @@ -15,28 +16,39 @@ callbacks.add( return message; } + // skips this callback if the message is a system message + if (message.t) { + return message; + } + // if the message has a token, it was sent by the visitor, so ignore it if (message.token) { return message; } - if (room.responseBy) { - await LivechatRooms.setAgentLastMessageTs(room._id); - } - // check if room is yet awaiting for response - if (!(typeof room.t !== 'undefined' && room.t === 'l' && room.waitingResponse)) { + // check if room is yet awaiting for response from visitor + if (!room.waitingResponse) { + // case where agent sends second message or any subsequent message in a room before visitor responds to the first message + // in this case, we just need to update the lastMessageTs of the responseBy object + if (room.responseBy) { + await LivechatRooms.setAgentLastMessageTs(room._id); + } return message; } - await LivechatRooms.setResponseByRoomId(room._id, { - user: { - _id: message.u._id, - username: message.u.username, - }, - }); + // This is the first message from agent after visitor had last responded + const responseBy: IOmnichannelRoom['responseBy'] = room.responseBy || { + _id: message.u._id, + username: message.u.username, + firstResponseTs: new Date(message.ts), + lastMessageTs: new Date(message.ts), + }; + + // this unsets waitingResponse and sets responseBy object + await LivechatRooms.setResponseByRoomId(room._id, responseBy); return message; }, - callbacks.priority.LOW, + callbacks.priority.HIGH, 'markRoomResponded', ); diff --git a/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts b/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts index c64f9e29c474..7cc8f8e6e9ad 100644 --- a/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts +++ b/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts @@ -1,9 +1,10 @@ -import { LivechatDepartment, Users, Rooms } from '@rocket.chat/models'; +import type { ILivechatDepartment } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatDepartment, Users, Rooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { sendMessage } from '../../../lib/server'; import { i18n } from '../../../../server/lib/i18n'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { settings } from '../../../settings/server'; callbacks.add( @@ -17,9 +18,12 @@ callbacks.add( let departmentName; const { name, email, department, message: text, host } = data; if (department && department !== '') { - const dept = await LivechatDepartment.findOneById(department, { - projection: { name: 1, offlineMessageChannelName: 1 }, - }); + const dept = await LivechatDepartment.findOneById>( + department, + { + projection: { name: 1, offlineMessageChannelName: 1 }, + }, + ); departmentName = dept?.name; if (dept?.offlineMessageChannelName) { channelName = dept.offlineMessageChannelName; @@ -30,7 +34,7 @@ callbacks.add( return data; } - const room: any = await Rooms.findOneByName(channelName, { projection: { t: 1, archived: 1 } }); + const room = await Rooms.findOneByName(channelName, { projection: { t: 1, archived: 1 } }); if (!room || room.archived || (isOmnichannelRoom(room) && room.closedAt)) { return data; } diff --git a/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts b/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts index 01d8417e000f..8a5a4c280670 100644 --- a/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts +++ b/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts @@ -1,10 +1,10 @@ -import moment from 'moment'; -import { LivechatBusinessHours, LivechatDepartment, Messages, LivechatRooms } from '@rocket.chat/models'; -import type { IOmnichannelRoom, IMessage, IBusinessHourWorkHour } from '@rocket.chat/core-typings'; +import type { IOmnichannelRoom, IMessage, IBusinessHourWorkHour, ILivechatDepartment } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatBusinessHours, LivechatDepartment, Messages, LivechatRooms } from '@rocket.chat/models'; +import moment from 'moment'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; import { businessHourManager } from '../business-hour'; const getSecondsWhenOfficeHoursIsDisabled = (room: IOmnichannelRoom, agentLastMessage: IMessage) => @@ -27,7 +27,11 @@ const getSecondsSinceLastAgentResponse = async (room: IOmnichannelRoom, agentLas return getSecondsWhenOfficeHoursIsDisabled(room, agentLastMessage); } let officeDays; - const department = room.departmentId ? await LivechatDepartment.findOneById(room.departmentId) : null; + const department = room.departmentId + ? await LivechatDepartment.findOneById>(room.departmentId, { + projection: { businessHourId: 1 }, + }) + : null; if (department?.businessHourId) { const businessHour = await LivechatBusinessHours.findOneById(department.businessHourId); if (!businessHour) { @@ -72,7 +76,7 @@ const getSecondsSinceLastAgentResponse = async (room: IOmnichannelRoom, agentLas callbacks.add( 'livechat.closeRoom', - async function (params) { + async (params) => { const { room } = params; if (!isOmnichannelRoom(room)) { diff --git a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts index 53a8c180c61b..ec584ec001d6 100644 --- a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts +++ b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts @@ -7,13 +7,13 @@ import { callbackLogger } from '../lib/logger'; callbacks.add( 'afterSaveMessage', - async function (message, room) { - callbackLogger.debug(`Calculating Omnichannel metrics for room ${room._id}`); + async (message, room) => { // check if room is livechat if (!isOmnichannelRoom(room)) { return message; } + callbackLogger.debug(`Calculating Omnichannel metrics for room ${room._id}`); // skips this callback if the message was edited if (!message || isEditedMessage(message)) { return message; diff --git a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts index 1dabee3b8c03..4bc28c3990ba 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts @@ -5,17 +5,18 @@ import { callbacks } from '../../../../lib/callbacks'; callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { if (!(isOmnichannelRoom(room) && room.v.token)) { return message; } if (message.t) { return message; } - if (message.token) { - await LivechatRooms.setVisitorLastMessageTimestampByRoomId(room._id, message.ts); + if (!message.token) { + return message; } - return message; + + await LivechatRooms.setVisitorLastMessageTimestampByRoomId(room._id, message.ts); }, callbacks.priority.HIGH, 'save-last-visitor-message-timestamp', diff --git a/apps/meteor/app/livechat/server/hooks/sendToCRM.ts b/apps/meteor/app/livechat/server/hooks/sendToCRM.ts index 54a277633788..d435da17e8ec 100644 --- a/apps/meteor/app/livechat/server/hooks/sendToCRM.ts +++ b/apps/meteor/app/livechat/server/hooks/sendToCRM.ts @@ -2,11 +2,11 @@ import type { IOmnichannelRoom, IOmnichannelSystemMessage, IMessage } from '@roc import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatRooms, Messages } from '@rocket.chat/models'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; +import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; import { Livechat } from '../lib/Livechat'; import { Livechat as LivechatTyped } from '../lib/LivechatTyped'; -import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; type AdditionalFields = | Record @@ -258,7 +258,7 @@ callbacks.add( callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { // only call webhook if it is a livechat room if (!isOmnichannelRoom(room) || !room?.v?.token) { return message; diff --git a/apps/meteor/app/livechat/server/index.ts b/apps/meteor/app/livechat/server/index.ts index 7955393acdb1..7d9167966953 100644 --- a/apps/meteor/app/livechat/server/index.ts +++ b/apps/meteor/app/livechat/server/index.ts @@ -15,6 +15,7 @@ import './hooks/sendEmailTranscriptOnClose'; import './hooks/saveContactLastChat'; import './hooks/saveLastMessageToInquiry'; import './hooks/afterUserActions'; +import './hooks/afterAgentRemoved'; import './methods/addAgent'; import './methods/addManager'; import './methods/changeLivechatStatus'; diff --git a/apps/meteor/app/livechat/server/lib/Analytics.js b/apps/meteor/app/livechat/server/lib/Analytics.js index e042a5c6a4b3..5f6e3469501e 100644 --- a/apps/meteor/app/livechat/server/lib/Analytics.js +++ b/apps/meteor/app/livechat/server/lib/Analytics.js @@ -1,11 +1,11 @@ -import moment from 'moment-timezone'; +import { Logger } from '@rocket.chat/logger'; import { LivechatRooms } from '@rocket.chat/models'; +import moment from 'moment-timezone'; -import { secondsToHHMMSS } from '../../../utils/server'; -import { getTimezone } from '../../../utils/server/lib/getTimezone'; -import { Logger } from '../../../logger/server'; -import { i18n } from '../../../../server/lib/i18n'; import { callbacks } from '../../../../lib/callbacks'; +import { secondsToHHMMSS } from '../../../../lib/utils/secondsToHHMMSS'; +import { i18n } from '../../../../server/lib/i18n'; +import { getTimezone } from '../../../utils/server/lib/getTimezone'; const HOURS_IN_DAY = 24; const logger = new Logger('OmnichannelAnalytics'); @@ -477,7 +477,7 @@ export const Analytics = { * @param {Boolean} [inv=false] reverse sort */ sortByValue(data, inv = false) { - data.sort(function (a, b) { + data.sort((a, b) => { // sort array if (parseFloat(a.value) > parseFloat(b.value)) { return inv ? -1 : 1; // if inv, reverse sort diff --git a/apps/meteor/app/livechat/server/lib/Contacts.ts b/apps/meteor/app/livechat/server/lib/Contacts.ts index 9fa441bd5a60..ac304f38487c 100644 --- a/apps/meteor/app/livechat/server/lib/Contacts.ts +++ b/apps/meteor/app/livechat/server/lib/Contacts.ts @@ -1,8 +1,8 @@ +import type { ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatVisitors, Users, LivechatRooms, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { MatchKeysAndValues, OnlyFieldsOfType } from 'mongodb'; -import { LivechatVisitors, Users, LivechatRooms, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models'; -import type { ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { callbacks } from '../../../../lib/callbacks'; import { trim } from '../../../../lib/utils/stringUtils'; diff --git a/apps/meteor/app/livechat/server/lib/Departments.ts b/apps/meteor/app/livechat/server/lib/Departments.ts index 349af6bd227d..0dd48a328fd1 100644 --- a/apps/meteor/app/livechat/server/lib/Departments.ts +++ b/apps/meteor/app/livechat/server/lib/Departments.ts @@ -1,7 +1,8 @@ +import type { ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { Logger } from '../../../logger/server'; class DepartmentHelperClass { logger = new Logger('Omnichannel:DepartmentHelper'); @@ -24,7 +25,10 @@ class DepartmentHelperClass { } this.logger.debug(`Department record removed: ${_id}`); - const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId(department._id) + const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId>( + department._id, + { projection: { agentId: 1 } }, + ) .cursor.map((agent) => agent.agentId) .toArray(); diff --git a/apps/meteor/app/livechat/server/lib/Helper.js b/apps/meteor/app/livechat/server/lib/Helper.ts similarity index 70% rename from apps/meteor/app/livechat/server/lib/Helper.js rename to apps/meteor/app/livechat/server/lib/Helper.ts index f1d30d910063..75722e709b17 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.js +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -1,9 +1,23 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { LivechatTransferEventType } from '@rocket.chat/apps-engine/definition/livechat'; -import { OmnichannelSourceType, DEFAULT_SLA_CONFIG } from '@rocket.chat/core-typings'; -import { LivechatPriorityWeight } from '@rocket.chat/core-typings/src/ILivechatPriority'; import { api, Message } from '@rocket.chat/core-services'; +import type { + ILivechatVisitor, + IOmnichannelRoom, + IMessage, + SelectedAgent, + ISubscription, + ILivechatInquiryRecord, + IUser, + TransferData, + ILivechatDepartmentAgents, + TransferByData, + ILivechatAgent, + ILivechatDepartment, +} from '@rocket.chat/core-typings'; +import { LivechatInquiryStatus, OmnichannelSourceType, DEFAULT_SLA_CONFIG, UserStatus } from '@rocket.chat/core-typings'; +import { LivechatPriorityWeight } from '@rocket.chat/core-typings/src/ILivechatPriority'; +import { Logger } from '@rocket.chat/logger'; +import type { InsertionModel } from '@rocket.chat/model-typings'; import { LivechatDepartmentAgents, LivechatInquiry, @@ -13,24 +27,24 @@ import { Rooms, Users, } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; -import { Livechat } from './Livechat'; -import { Livechat as LivechatTyped } from './LivechatTyped'; -import { RoutingManager } from './RoutingManager'; -import { callbacks } from '../../../../lib/callbacks'; -import { Logger } from '../../../logger/server'; -import { settings } from '../../../settings/server'; import { Apps, AppEvents } from '../../../../ee/server/apps'; +import { callbacks } from '../../../../lib/callbacks'; +import { validateEmail as validatorFunc } from '../../../../lib/emailValidator'; +import { i18n } from '../../../../server/lib/i18n'; +import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { sendNotification } from '../../../lib/server'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; +import { settings } from '../../../settings/server'; +import { Livechat } from './Livechat'; +import { Livechat as LivechatTyped } from './LivechatTyped'; import { queueInquiry, saveQueueInquiry } from './QueueManager'; -import { validateEmail as validatorFunc } from '../../../../lib/emailValidator'; -import { i18n } from '../../../../server/lib/i18n'; +import { RoutingManager } from './RoutingManager'; const logger = new Logger('LivechatHelper'); - -export const allowAgentSkipQueue = (agent) => { +export const allowAgentSkipQueue = (agent: SelectedAgent) => { check( agent, Match.ObjectIncluding({ @@ -40,8 +54,13 @@ export const allowAgentSkipQueue = (agent) => { return hasRoleAsync(agent.agentId, 'bot'); }; - -export const createLivechatRoom = async (rid, name, guest, roomInfo = {}, extraData = {}) => { +export const createLivechatRoom = async ( + rid: string, + name: string, + guest: ILivechatVisitor, + roomInfo: Partial = {}, + extraData = {}, +) => { check(rid, String); check(name, String); check( @@ -60,14 +79,14 @@ export const createLivechatRoom = async (rid, name, guest, roomInfo = {}, extraD logger.debug(`Creating livechat room for visitor ${_id}`); - const room = Object.assign( + const room: InsertionModel = Object.assign( { _id: rid, msgs: 0, usersCount: 1, lm: newRoomAt, fname: name, - t: 'l', + t: 'l' as const, ts: newRoomAt, departmentId, v: { @@ -95,7 +114,7 @@ export const createLivechatRoom = async (rid, name, guest, roomInfo = {}, extraD const roomId = (await Rooms.insertOne(room)).insertedId; - Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room); + void Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room); await callbacks.run('livechat.newRoom', room); await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room); @@ -103,7 +122,21 @@ export const createLivechatRoom = async (rid, name, guest, roomInfo = {}, extraD return roomId; }; -export const createLivechatInquiry = async ({ rid, name, guest, message, initialStatus, extraData = {} }) => { +export const createLivechatInquiry = async ({ + rid, + name, + guest, + message, + initialStatus, + extraData, +}: { + rid: string; + name?: string; + guest?: Pick; + message?: Pick; + initialStatus?: LivechatInquiryStatus; + extraData?: Pick; +}) => { check(rid, String); check(name, String); check( @@ -124,19 +157,19 @@ export const createLivechatInquiry = async ({ rid, name, guest, message, initial const extraInquiryInfo = await callbacks.run('livechat.beforeInquiry', extraData); - const { _id, username, token, department, status = 'online' } = guest; + const { _id, username, token, department, status = UserStatus.ONLINE } = guest; const { msg } = message; const ts = new Date(); logger.debug(`Creating livechat inquiry for visitor ${_id}`); - const inquiry = { + const inquiry: InsertionModel = { rid, name, ts, department, message: msg, - status: initialStatus || 'ready', + status: initialStatus || LivechatInquiryStatus.READY, v: { _id, username, @@ -156,7 +189,13 @@ export const createLivechatInquiry = async ({ rid, name, guest, message, initial return result; }; -export const createLivechatSubscription = async (rid, name, guest, agent, department) => { +export const createLivechatSubscription = async ( + rid: string, + name: string, + guest: Pick, + agent: SelectedAgent, + department?: string, +) => { check(rid, String); check(name, String); check( @@ -180,11 +219,14 @@ export const createLivechatSubscription = async (rid, name, guest, agent, depart return existingSubscription; } - const { _id, username, token, status = 'online' } = guest; + const { _id, username, token, status = UserStatus.ONLINE } = guest; - const subscriptionData = { + const subscriptionData: InsertionModel = { rid, + name, fname: name, + lowerCaseName: name.toLowerCase(), + lowerCaseFName: name.toLowerCase(), alert: true, open: true, unread: 1, @@ -204,31 +246,38 @@ export const createLivechatSubscription = async (rid, name, guest, agent, depart token, status, }, + ts: new Date(), + lr: new Date(), + ls: new Date(), ...(department && { department }), }; return Subscriptions.insertOne(subscriptionData); }; -export const removeAgentFromSubscription = async (rid, { _id, username }) => { +export const removeAgentFromSubscription = async (rid: string, { _id, username }: Pick) => { const room = await LivechatRooms.findOneById(rid); const user = await Users.findOneById(_id); + if (!room || !user) { + return; + } + await Subscriptions.removeByRoomIdAndUserId(rid, _id); - await Message.saveSystemMessage('ul', rid, username, { _id: user._id, username: user.username, name: user.name }); + await Message.saveSystemMessage('ul', rid, username || '', { _id: user._id, username: user.username, name: user.name }); setImmediate(() => { - Apps.triggerEvent(AppEvents.IPostLivechatAgentUnassigned, { room, user }); + void Apps.triggerEvent(AppEvents.IPostLivechatAgentUnassigned, { room, user }); }); }; -export const parseAgentCustomFields = (customFields) => { +export const parseAgentCustomFields = (customFields?: Record) => { if (!customFields) { return; } const externalCustomFields = () => { - const accountCustomFields = settings.get('Accounts_CustomFields'); + const accountCustomFields = settings.get('Accounts_CustomFields'); if (!accountCustomFields || accountCustomFields.trim() === '') { return []; } @@ -245,11 +294,11 @@ export const parseAgentCustomFields = (customFields) => { const externalCF = externalCustomFields(); return Object.keys(customFields).reduce( (newObj, key) => (externalCF.includes(key) ? { ...newObj, [key]: customFields[key] } : newObj), - null, + {}, ); }; -export const normalizeAgent = async (agentId) => { +export const normalizeAgent = async (agentId?: string) => { if (!agentId) { return; } @@ -259,13 +308,17 @@ export const normalizeAgent = async (agentId) => { } const agent = await Users.getAgentInfo(agentId, settings.get('Livechat_show_agent_email')); + if (!agent) { + return; + } + const { customFields: agentCustomFields, ...extraData } = agent; const customFields = parseAgentCustomFields(agentCustomFields); - return Object.assign(extraData, { ...(customFields && { customFields }) }); + return Object.assign(extraData, { ...(customFields && { customFields }) }) as ILivechatAgent; }; -export const dispatchAgentDelegated = async (rid, agentId) => { +export const dispatchAgentDelegated = async (rid: string, agentId?: string) => { const agent = await normalizeAgent(agentId); void api.broadcast('omnichannel.room', rid, { @@ -274,7 +327,7 @@ export const dispatchAgentDelegated = async (rid, agentId) => { }); }; -export const dispatchInquiryQueued = async (inquiry, agent) => { +export const dispatchInquiryQueued = async (inquiry: ILivechatInquiryRecord, agent?: SelectedAgent | null) => { if (!inquiry?._id) { return; } @@ -282,9 +335,13 @@ export const dispatchInquiryQueued = async (inquiry, agent) => { const { department, rid, v } = inquiry; const room = await LivechatRooms.findOneById(rid); + if (!room) { + return; + } + setImmediate(() => callbacks.run('livechat.chatQueued', room)); - if (RoutingManager.getConfig().autoAssignAgent) { + if (RoutingManager.getConfig()?.autoAssignAgent) { return; } @@ -325,6 +382,8 @@ export const dispatchInquiryQueued = async (inquiry, agent) => { }, sender: v, hasMentionToAll: true, // consider all agents to be in the room + hasReplyToThread: false, + disableAllMessageNotifications: false, hasMentionToHere: false, message: Object.assign({}, { u: v }), // we should use server's language for this type of messages instead of user's @@ -335,8 +394,8 @@ export const dispatchInquiryQueued = async (inquiry, agent) => { } }; -export const forwardRoomToAgent = async (room, transferData) => { - if (!room || !room.open) { +export const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: TransferData) => { + if (!room?.open) { return false; } @@ -350,7 +409,7 @@ export const forwardRoomToAgent = async (room, transferData) => { } const { _id: rid, servedBy: oldServedBy } = room; - const inquiry = await LivechatInquiry.findOneByRoomId(rid); + const inquiry = await LivechatInquiry.findOneByRoomId(rid, {}); if (!inquiry) { logger.debug(`No inquiries found for room ${room._id}. Cannot forward`); throw new Error('error-invalid-inquiry'); @@ -363,10 +422,10 @@ export const forwardRoomToAgent = async (room, transferData) => { const { username } = user; const agent = { agentId, username }; // Remove department from inquiry to make sure the routing algorithm treat this as forwarding to agent and not as forwarding to department - inquiry.department = undefined; + delete inquiry.department; // There are some Enterprise features that may interrupt the forwarding process // Due to that we need to check whether the agent has been changed or not - logger.debug(`Forwarding inquiry ${inquiry._id} to agent ${agent._id}`); + logger.debug(`Forwarding inquiry ${inquiry._id} to agent ${agent.agentId}`); const roomTaken = await RoutingManager.takeInquiry(inquiry, agent, { ...(clientAction && { clientAction }), }); @@ -384,7 +443,7 @@ export const forwardRoomToAgent = async (room, transferData) => { } setImmediate(() => { - Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { + void Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { type: LivechatTransferEventType.AGENT, room: rid, from: oldServedBy?._id, @@ -393,18 +452,28 @@ export const forwardRoomToAgent = async (room, transferData) => { }); } - logger.debug(`Inquiry ${inquiry._id} taken by agent ${agent._id}`); + logger.debug(`Inquiry ${inquiry._id} taken by agent ${agent.agentId}`); await callbacks.run('livechat.afterForwardChatToAgent', { rid, servedBy, oldServedBy }); return true; }; -export const updateChatDepartment = async ({ rid, newDepartmentId, oldDepartmentId }) => { - await LivechatRooms.changeDepartmentIdByRoomId(rid, newDepartmentId); - await LivechatInquiry.changeDepartmentIdByRoomId(rid, newDepartmentId); - await Subscriptions.changeDepartmentByRoomId(rid, newDepartmentId); +export const updateChatDepartment = async ({ + rid, + newDepartmentId, + oldDepartmentId, +}: { + rid: string; + newDepartmentId: string; + oldDepartmentId?: string; +}) => { + await Promise.all([ + LivechatRooms.changeDepartmentIdByRoomId(rid, newDepartmentId), + LivechatInquiry.changeDepartmentIdByRoomId(rid, newDepartmentId), + Subscriptions.changeDepartmentByRoomId(rid, newDepartmentId), + ]); setImmediate(() => { - Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { + void Apps.triggerEvent(AppEvents.IPostLivechatRoomTransferred, { type: LivechatTransferEventType.DEPARTMENT, room: rid, from: oldDepartmentId, @@ -419,8 +488,8 @@ export const updateChatDepartment = async ({ rid, newDepartmentId, oldDepartment }); }; -export const forwardRoomToDepartment = async (room, guest, transferData) => { - if (!room || !room.open) { +export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILivechatVisitor, transferData: TransferData) => { + if (!room?.open) { return false; } logger.debug(`Attempting to forward room ${room._id} to department ${transferData.departmentId}`); @@ -429,13 +498,17 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { const { _id: rid, servedBy: oldServedBy, departmentId: oldDepartmentId } = room; let agent = null; - const inquiry = await LivechatInquiry.findOneByRoomId(rid); + const inquiry = await LivechatInquiry.findOneByRoomId(rid, {}); if (!inquiry) { logger.debug(`Cannot forward room ${room._id}. No inquiries found`); throw new Error('error-transferring-inquiry'); } const { departmentId } = transferData; + if (!departmentId) { + logger.debug(`Cannot forward room ${room._id}. No departmentId provided`); + throw new Error('error-transferring-inquiry-no-department'); + } if (oldDepartmentId === departmentId) { throw new Error('error-forwarding-chat-same-department'); } @@ -443,19 +516,21 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { const { userId: agentId, clientAction } = transferData; if (agentId) { logger.debug(`Forwarding room ${room._id} to department ${departmentId} (to user ${agentId})`); - let user = await Users.findOneOnlineAgentById(agentId); + const user = await Users.findOneOnlineAgentById(agentId); if (!user) { throw new Error('error-user-is-offline'); } - user = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId); - if (!user) { + const isInDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId, { + projection: { _id: 1 }, + }); + if (!isInDepartment) { throw new Error('error-user-not-belong-to-department'); } const { username } = user; agent = { agentId, username }; } - if (!RoutingManager.getConfig().autoAssignAgent) { + if (!RoutingManager.getConfig()?.autoAssignAgent) { logger.debug( `Routing algorithm doesn't support auto assignment (using ${RoutingManager.methodName}). Chat will be on department queue`, ); @@ -477,23 +552,32 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { const { servedBy, chatQueued } = roomTaken; if (!chatQueued && oldServedBy && servedBy && oldServedBy._id === servedBy._id) { - const department = await LivechatDepartment.findOneById(departmentId); + const department = departmentId + ? await LivechatDepartment.findOneById>(departmentId, { + projection: { fallbackForwardDepartment: 1 }, + }) + : null; if (!department?.fallbackForwardDepartment?.length) { logger.debug(`Cannot forward room ${room._id}. Chat assigned to agent ${servedBy._id} (Previous was ${oldServedBy._id})`); throw new Error('error-no-agents-online-in-department'); } // if a chat has a fallback department, attempt to redirect chat to there [EE] - return !!callbacks.run('livechat:onTransferFailure', { room, guest, transferData }); + const transferSuccess = !!(await callbacks.run('livechat:onTransferFailure', room, { guest, transferData })); + // On CE theres no callback so it will return the room + if (typeof transferSuccess !== 'boolean' || !transferSuccess) { + logger.debug(`Cannot forward room ${room._id}. Unable to delegate inquiry`); + return false; + } } await Livechat.saveTransferHistory(room, transferData); if (oldServedBy) { // if chat is queued then we don't ignore the new servedBy agent bcs at this // point the chat is not assigned to him/her and it is still in the queue - await RoutingManager.removeAllRoomSubscriptions(room, !chatQueued && servedBy); + await RoutingManager.removeAllRoomSubscriptions(room, !chatQueued ? servedBy : undefined); } if (!chatQueued && servedBy) { - await Message.saveSystemMessage('uj', rid, servedBy.username, servedBy); + await Message.saveSystemMessage('uj', rid, servedBy.username || '', servedBy); } await updateChatDepartment({ rid, newDepartmentId: departmentId, oldDepartmentId }); @@ -502,10 +586,14 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { logger.debug(`Forwarding succesful. Marking inquiry ${inquiry._id} as ready`); await LivechatInquiry.readyInquiry(inquiry._id); await LivechatRooms.removeAgentByRoomId(rid); - await dispatchAgentDelegated(rid, null); + await dispatchAgentDelegated(rid); const newInquiry = await LivechatInquiry.findOneById(inquiry._id); - await queueInquiry(newInquiry); + if (!newInquiry) { + logger.debug(`Inquiry ${inquiry._id} not found`); + throw new Error('error-invalid-inquiry'); + } + await queueInquiry(newInquiry); logger.debug(`Inquiry ${inquiry._id} queued succesfully`); } @@ -516,7 +604,7 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { return true; }; -export const normalizeTransferredByData = (transferredBy, room) => { +export const normalizeTransferredByData = (transferredBy: TransferByData, room: IOmnichannelRoom) => { if (!transferredBy || !room) { throw new Error('You must provide "transferredBy" and "room" params to "getTransferredByData"'); } @@ -531,7 +619,7 @@ export const normalizeTransferredByData = (transferredBy, room) => { }; }; -export const checkServiceStatus = async ({ guest, agent }) => { +export const checkServiceStatus = async ({ guest, agent }: { guest: Pick; agent?: SelectedAgent }) => { if (!agent) { return LivechatTyped.online(guest.department); } @@ -541,7 +629,21 @@ export const checkServiceStatus = async ({ guest, agent }) => { return users > 0; }; -export const updateDepartmentAgents = async (departmentId, agents, departmentEnabled) => { +const parseFromIntOrStr = (value: string | number) => { + if (typeof value === 'number') { + return value; + } + return parseInt(value); +}; + +export const updateDepartmentAgents = async ( + departmentId: string, + agents: { + upsert?: Pick[]; + remove?: Pick[]; + }, + departmentEnabled: boolean, +) => { check(departmentId, String); check( agents, @@ -572,9 +674,9 @@ export const updateDepartmentAgents = async (departmentId, agents, departmentEna await LivechatDepartmentAgents.saveAgent({ agentId: agent.agentId, departmentId, - username: agentFromDb.username, - count: agent.count ? parseInt(agent.count) : 0, - order: agent.order ? parseInt(agent.order) : 0, + username: agentFromDb.username || '', + count: agent.count ? parseFromIntOrStr(agent.count) : 0, + order: agent.order ? parseFromIntOrStr(agent.order) : 0, departmentEnabled, }); agentsAdded.push(agent.agentId); @@ -595,7 +697,7 @@ export const updateDepartmentAgents = async (departmentId, agents, departmentEna return true; }; -export const validateEmail = (email) => { +export const validateEmail = (email: string) => { if (!validatorFunc(email)) { throw new Meteor.Error('error-invalid-email', `Invalid email ${email}`, { function: 'Livechat.validateEmail', diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 4addbc24eec2..52138740e295 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -4,9 +4,8 @@ import dns from 'dns'; import util from 'util'; -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import UAParser from 'ua-parser-js'; +import { Message, VideoConf, api } from '@rocket.chat/core-services'; +import { Logger } from '@rocket.chat/logger'; import { LivechatVisitors, LivechatCustomField, @@ -19,29 +18,31 @@ import { LivechatDepartmentAgents, Rooms, Users, + ReadReceipts, } from '@rocket.chat/models'; -import { Message, VideoConf, api } from '@rocket.chat/core-services'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import UAParser from 'ua-parser-js'; -import { RoutingManager } from './RoutingManager'; -import { Analytics } from './Analytics'; -import { settings } from '../../../settings/server'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; -import { Logger } from '../../../logger/server'; +import { trim } from '../../../../lib/utils/stringUtils'; +import { i18n } from '../../../../server/lib/i18n'; +import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; +import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles'; import { canAccessRoomAsync, roomAccessAttributes } from '../../../authorization/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import * as Mailer from '../../../mailer/server/api'; +import { FileUpload } from '../../../file-upload/server'; +import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { updateMessage } from '../../../lib/server/functions/updateMessage'; -import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; -import { FileUpload } from '../../../file-upload/server'; -import { normalizeTransferredByData, parseAgentCustomFields, updateDepartmentAgents } from './Helper'; -import { Apps, AppEvents } from '../../../../ee/server/apps'; +import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; import { businessHourManager } from '../business-hour'; -import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; -import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles'; -import { trim } from '../../../../lib/utils/stringUtils'; +import { Analytics } from './Analytics'; +import { normalizeTransferredByData, parseAgentCustomFields, updateDepartmentAgents } from './Helper'; import { Livechat as LivechatTyped } from './LivechatTyped'; -import { i18n } from '../../../../server/lib/i18n'; +import { RoutingManager } from './RoutingManager'; const logger = new Logger('Livechat'); @@ -564,18 +565,14 @@ export const Livechat = { }, async afterRemoveAgent(user) { - await Promise.all([ - Users.removeAgent(user._id), - LivechatDepartmentAgents.removeByAgentId(user._id), - LivechatVisitors.removeContactManagerByUsername(user.username), - ]); + await callbacks.run('livechat.afterAgentRemoved', { agent: user }); return true; }, async removeAgent(username) { check(username, String); - const user = await Users.findOneByUsername(username, { projection: { _id: 1 } }); + const user = await Users.findOneByUsername(username, { projection: { _id: 1, username: 1 } }); if (!user) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { @@ -645,13 +642,18 @@ export const Livechat = { const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {}); const cursor = LivechatRooms.findByVisitorToken(token, extraQuery); for await (const room of cursor) { - await FileUpload.removeFilesByRoomId(room._id); - await Messages.removeByRoomId(room._id); + await Promise.all([ + FileUpload.removeFilesByRoomId(room._id), + Messages.removeByRoomId(room._id), + ReadReceipts.removeByRoomId(room._id), + ]); } - await Subscriptions.removeByVisitorToken(token); - await LivechatRooms.removeByVisitorToken(token); - await LivechatInquiry.removeByVisitorToken(token); + await Promise.all([ + Subscriptions.removeByVisitorToken(token), + LivechatRooms.removeByVisitorToken(token), + LivechatInquiry.removeByVisitorToken(token), + ]); }, async saveDepartmentAgents(_id, departmentAgents) { @@ -707,7 +709,9 @@ export const Livechat = { }); } const ret = (await LivechatDepartmentRaw.removeById(_id)).deletedCount; - const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id).toArray()).map((agent) => agent.agentId); + const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id, { projection: { agentId: 1 } }).toArray()).map( + (agent) => agent.agentId, + ); await LivechatDepartmentAgents.removeByDepartmentId(_id); await LivechatDepartmentRaw.unsetFallbackDepartmentByDepartmentId(_id); if (ret) { @@ -837,7 +841,7 @@ export const Livechat = { async sendOfflineMessage(data = {}) { if (!settings.get('Livechat_display_offline_form')) { - return false; + throw new Error('error-offline-form-disabled'); } const { message, name, email, department, host } = data; @@ -872,6 +876,10 @@ export const Livechat = { } } + // TODO Block offline form if Livechat_offline_email is undefined + // (it does not make sense to have an offline form that does nothing) + // `this.sendEmail` will throw an error if the email is invalid + // thus this breaks livechat, since the "to" email is invalid, and that returns an [invalid email] error to the livechat client let emailTo = settings.get('Livechat_offline_email'); if (department && department !== '') { const dep = await LivechatDepartmentRaw.findOneByIdOrName(department); @@ -886,8 +894,6 @@ export const Livechat = { setImmediate(() => { callbacks.run('livechat.offlineMessage', data); }); - - return true; }, async notifyAgentStatusChanged(userId, status) { diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 1b3896bec796..1c60a257d319 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1,3 +1,4 @@ +import { Message } from '@rocket.chat/core-services'; import type { IOmnichannelRoom, IOmnichannelRoomClosingInfo, @@ -8,8 +9,10 @@ import type { SelectedAgent, ILivechatAgent, IMessage, + ILivechatDepartment, } from '@rocket.chat/core-typings'; -import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { UserStatus, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { Logger, type MainLogger } from '@rocket.chat/logger'; import { LivechatDepartment, LivechatInquiry, @@ -19,27 +22,25 @@ import { Messages, Users, LivechatDepartmentAgents, + ReadReceipts, } from '@rocket.chat/models'; -import { Message } from '@rocket.chat/core-services'; +import { Random } from '@rocket.chat/random'; +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import moment from 'moment-timezone'; import type { FindCursor, UpdateFilter } from 'mongodb'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { Random } from '@rocket.chat/random'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; -import { Logger } from '../../../logger/server'; +import { i18n } from '../../../../server/lib/i18n'; +import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; -import { Apps, AppEvents } from '../../../../ee/server/apps'; -import { getTimezone } from '../../../utils/server/lib/getTimezone'; -import { settings } from '../../../settings/server'; import * as Mailer from '../../../mailer/server/api'; -import { RoutingManager } from './RoutingManager'; -import { QueueManager } from './QueueManager'; -import { updateDepartmentAgents, validateEmail } from './Helper'; -import type { MainLogger } from '../../../../server/lib/logger/getPino'; import { metrics } from '../../../metrics/server'; -import { i18n } from '../../../../server/lib/i18n'; -import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; +import { settings } from '../../../settings/server'; +import { getTimezone } from '../../../utils/server/lib/getTimezone'; +import { updateDepartmentAgents, validateEmail } from './Helper'; +import { QueueManager } from './QueueManager'; +import { RoutingManager } from './RoutingManager'; type GenericCloseRoomParams = { room: IOmnichannelRoom; @@ -125,7 +126,7 @@ class LivechatClass { return RoutingManager.getNextAgent(department); } - async getOnlineAgents(department?: string, agent?: SelectedAgent): Promise | undefined> { + async getOnlineAgents(department?: string, agent?: SelectedAgent | null): Promise | undefined> { if (agent?.agentId) { return Users.findOnlineAgents(agent.agentId); } @@ -156,6 +157,11 @@ class LivechatClass { return; } + const commentRequired = settings.get('Livechat_request_comment_when_closing_conversation'); + if (commentRequired && !comment?.trim()) { + throw new Error('error-comment-is-required'); + } + const { updatedOptions: options } = await this.resolveChatTags(room, params.options); this.logger.debug(`Resolved chat tags for room ${room._id}`); @@ -294,7 +300,10 @@ class LivechatClass { room = null; } - if (guest.department && !(await LivechatDepartment.findOneById(guest.department))) { + if ( + guest.department && + !(await LivechatDepartment.findOneById>(guest.department, { projection: { _id: 1 } })) + ) { await LivechatVisitors.removeDepartmentById(guest._id); const tmpGuest = await LivechatVisitors.findOneById(guest._id); if (tmpGuest) { @@ -352,7 +361,9 @@ class LivechatClass { return onlineForDep; } - const dep = await LivechatDepartment.findOneById(department); + const dep = await LivechatDepartment.findOneById>(department, { + projection: { fallbackForwardDepartment: 1 }, + }); if (!dep?.fallbackForwardDepartment) { return onlineForDep; } @@ -397,6 +408,7 @@ class LivechatClass { const result = await Promise.allSettled([ Messages.removeByRoomId(rid), + ReadReceipts.removeByRoomId(rid), Subscriptions.removeByRoomId(rid), LivechatInquiry.removeByRoomId(rid), LivechatRooms.removeById(rid), @@ -540,7 +552,7 @@ class LivechatClass { phone, username, connectionData, - status = 'online', + status = UserStatus.ONLINE, }: { id?: string; token: string; @@ -580,7 +592,7 @@ class LivechatClass { if (department) { Livechat.logger.debug(`Attempt to find a department with id/name ${department}`); - const dep = await LivechatDepartment.findOneByIdOrName(department); + const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { _id: 1 } }); if (!dep) { Livechat.logger.debug('Invalid department provided'); throw new Meteor.Error('error-invalid-department', 'The provided department is invalid'); @@ -667,7 +679,12 @@ class LivechatClass { }; } - const department = await LivechatDepartment.findOneById(departmentId); + const department = await LivechatDepartment.findOneById>( + departmentId, + { + projection: { requestTagBeforeClosingChat: 1, chatClosingTags: 1 }, + }, + ); if (!department) { return { updatedOptions: { @@ -781,7 +798,7 @@ class LivechatClass { return updateDepartmentAgents( dep._id, { - ...(toRemoveIds.includes(dep._id) ? { remove: [{ agentId: _id }] } : { upsert: [{ agentId: _id }] }), + ...(toRemoveIds.includes(dep._id) ? { remove: [{ agentId: _id }] } : { upsert: [{ agentId: _id, count: 0, order: 0 }] }), }, dep.enabled, ); diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.js b/apps/meteor/app/livechat/server/lib/QueueManager.ts similarity index 60% rename from apps/meteor/app/livechat/server/lib/QueueManager.js rename to apps/meteor/app/livechat/server/lib/QueueManager.ts index 644c8833cd6c..597f38b71ec0 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.js +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -1,33 +1,53 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; +import type { ILivechatInquiryRecord, ILivechatVisitor, IMessage, IOmnichannelRoom, SelectedAgent } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import { LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { checkServiceStatus, createLivechatRoom, createLivechatInquiry } from './Helper'; import { callbacks } from '../../../../lib/callbacks'; -import { Logger } from '../../../logger/server'; +import { checkServiceStatus, createLivechatRoom, createLivechatInquiry } from './Helper'; import { RoutingManager } from './RoutingManager'; const logger = new Logger('QueueManager'); -export const saveQueueInquiry = async (inquiry) => { +export const saveQueueInquiry = async (inquiry: ILivechatInquiryRecord) => { await LivechatInquiry.queueInquiry(inquiry._id); await callbacks.run('livechat.afterInquiryQueued', inquiry); }; -export const queueInquiry = async (inquiry, defaultAgent) => { +export const queueInquiry = async (inquiry: ILivechatInquiryRecord, defaultAgent?: SelectedAgent) => { const inquiryAgent = await RoutingManager.delegateAgent(defaultAgent, inquiry); logger.debug(`Delegating inquiry with id ${inquiry._id} to agent ${defaultAgent?.username}`); await callbacks.run('livechat.beforeRouteChat', inquiry, inquiryAgent); - inquiry = await LivechatInquiry.findOneById(inquiry._id); + const dbInquiry = await LivechatInquiry.findOneById(inquiry._id); + + if (!dbInquiry) { + logger.error(`Inquiry with id ${inquiry._id} not found`); + throw new Error('inquiry-not-found'); + } - if (inquiry.status === 'ready') { + if (dbInquiry.status === 'ready') { logger.debug(`Inquiry with id ${inquiry._id} is ready. Delegating to agent ${inquiryAgent?.username}`); - return RoutingManager.delegateInquiry(inquiry, inquiryAgent); + return RoutingManager.delegateInquiry(dbInquiry, inquiryAgent); } }; -export const QueueManager = { +type queueManager = { + requestRoom: (params: { + guest: ILivechatVisitor; + message: Pick; + roomInfo: { + source?: IOmnichannelRoom['source']; + [key: string]: unknown; + }; + agent?: SelectedAgent; + extraData?: Record; + }) => Promise; + unarchiveRoom: (archivedRoom?: IOmnichannelRoom) => Promise; +}; + +export const QueueManager: queueManager = { async requestRoom({ guest, message, roomInfo, agent, extraData }) { logger.debug(`Requesting a room for guest ${guest._id}`); check( @@ -43,6 +63,7 @@ export const QueueManager = { username: String, status: Match.Maybe(String), department: Match.Maybe(String), + name: Match.Maybe(String), }), ); @@ -52,9 +73,13 @@ export const QueueManager = { } const { rid } = message; - const name = (roomInfo && roomInfo.fname) || guest.name || guest.username; + const name = (roomInfo?.fname as string) || guest.name || guest.username; const room = await LivechatRooms.findOneById(await createLivechatRoom(rid, name, guest, roomInfo, extraData)); + if (!room) { + logger.error(`Room for visitor ${guest._id} not found`); + throw new Error('room-not-found'); + } logger.debug(`Room for visitor ${guest._id} created with id ${room._id}`); const inquiry = await LivechatInquiry.findOneById( @@ -66,6 +91,11 @@ export const QueueManager = { extraData: { ...extraData, source: roomInfo.source }, }), ); + if (!inquiry) { + logger.error(`Inquiry for visitor ${guest._id} not found`); + throw new Error('inquiry-not-found'); + } + logger.debug(`Generated inquiry for visitor ${guest._id} with id ${inquiry._id} [Not queued]`); await LivechatRooms.updateRoomCount(); @@ -82,18 +112,13 @@ export const QueueManager = { return newRoom; }, - async unarchiveRoom(archivedRoom = {}) { - const { - _id: rid, - open, - closedAt, - fname: name, - servedBy, - v, - departmentId: department, - lastMessage: message, - source = {}, - } = archivedRoom; + async unarchiveRoom(archivedRoom) { + if (!archivedRoom) { + logger.error('No room to unarchive'); + throw new Error('no-room-to-unarchive'); + } + + const { _id: rid, open, closedAt, fname: name, servedBy, v, departmentId: department, lastMessage: message, source } = archivedRoom; if (!rid || !closedAt || !!open) { return archivedRoom; @@ -101,7 +126,7 @@ export const QueueManager = { logger.debug(`Attempting to unarchive room with id ${rid}`); - const oldInquiry = await LivechatInquiry.findOneByRoomId(rid); + const oldInquiry = await LivechatInquiry.findOneByRoomId>(rid, { projection: { _id: 1 } }); if (oldInquiry) { logger.debug(`Removing old inquiry (${oldInquiry._id}) for room ${rid}`); await LivechatInquiry.removeByRoomId(rid); @@ -112,8 +137,8 @@ export const QueueManager = { ...(department && { department }), }; - let defaultAgent; - if (servedBy && (await Users.findOneOnlineAgentByUserList(servedBy.username))) { + let defaultAgent: SelectedAgent | undefined; + if (servedBy?.username && (await Users.findOneOnlineAgentByUserList(servedBy.username))) { defaultAgent = { agentId: servedBy._id, username: servedBy.username }; } @@ -124,6 +149,11 @@ export const QueueManager = { throw new Error('room-not-found'); } const inquiry = await LivechatInquiry.findOneById(await createLivechatInquiry({ rid, name, guest, message, extraData: { source } })); + if (!inquiry) { + logger.error(`Inquiry for visitor ${guest._id} not found`); + throw new Error('inquiry-not-found'); + } + logger.debug(`Generated inquiry for visitor ${v._id} with id ${inquiry._id} [Not queued]`); await queueInquiry(inquiry, defaultAgent); diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.ts b/apps/meteor/app/livechat/server/lib/RoutingManager.ts index 55c0bff01c66..0e975ca06763 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.ts +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.ts @@ -1,7 +1,4 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { LivechatInquiry, LivechatRooms, Subscriptions, Rooms, Users } from '@rocket.chat/models'; -import { Message } from '@rocket.chat/core-services'; +import { Message, Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatInquiryRecord, ILivechatVisitor, @@ -11,8 +8,15 @@ import type { RoutingMethodConfig, SelectedAgent, InquiryWithAgentInfo, + TransferData, } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; +import { LivechatInquiry, LivechatRooms, Subscriptions, Rooms, Users } from '@rocket.chat/models'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; +import { callbacks } from '../../../../lib/callbacks'; import { createLivechatSubscription, dispatchAgentDelegated, @@ -23,9 +27,6 @@ import { updateChatDepartment, allowAgentSkipQueue, } from './Helper'; -import { callbacks } from '../../../../lib/callbacks'; -import { Logger } from '../../../../server/lib/logger/Logger'; -import { Apps, AppEvents } from '../../../../ee/server/apps'; const logger = new Logger('RoutingManager'); @@ -34,7 +35,7 @@ type Routing = { methods: Record; startQueue(): void; isMethodSet(): boolean; - setMethodNameAndStartQueue(name: string): void; + setMethodNameAndStartQueue(name: string): Promise; registerMethod(name: string, Method: IRoutingMethodConstructor): void; getMethod(): IRoutingMethod; getConfig(): RoutingMethodConfig | undefined; @@ -42,8 +43,8 @@ type Routing = { delegateInquiry( inquiry: InquiryWithAgentInfo, agent?: SelectedAgent | null, - options?: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId: string; transferData: any } }, - ): Promise; + options?: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId?: string; transferData?: any } }, + ): Promise<(IOmnichannelRoom & { chatQueued?: boolean }) | null | void>; assignAgent(inquiry: InquiryWithAgentInfo, agent: SelectedAgent): Promise; unassignAgent(inquiry: ILivechatInquiryRecord, departmentId?: string): Promise; takeInquiry( @@ -52,18 +53,10 @@ type Routing = { 'estimatedInactivityCloseTimeAt' | 'message' | 't' | 'source' | 'estimatedWaitingTimeQueue' | 'priorityWeight' | '_updatedAt' >, agent: SelectedAgent | null, - options?: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId: string; transferData: any } }, + options?: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId?: string; transferData?: any } }, ): Promise; - transferRoom( - room: IOmnichannelRoom, - guest: ILivechatVisitor, - transferData: { - departmentId?: string; - userId?: string; - transferredBy: { _id: string }; - }, - ): Promise; - delegateAgent(agent: SelectedAgent, inquiry: ILivechatInquiryRecord): Promise; + transferRoom(room: IOmnichannelRoom, guest: ILivechatVisitor, transferData: TransferData): Promise; + delegateAgent(agent: SelectedAgent | undefined, inquiry: ILivechatInquiryRecord): Promise; removeAllRoomSubscriptions(room: Pick, ignoreUser?: { _id: string }): Promise; }; @@ -80,7 +73,7 @@ export const RoutingManager: Routing = { return !!this.methodName; }, - setMethodNameAndStartQueue(name) { + async setMethodNameAndStartQueue(name) { logger.debug(`Changing default routing method from ${this.methodName} to ${name}`); if (!this.methods[name]) { logger.warn(`Cannot change routing method to ${name}. Selected Routing method does not exists. Defaulting to Manual_Selection`); @@ -89,7 +82,7 @@ export const RoutingManager: Routing = { this.methodName = name; } - this.startQueue(); + void (await Omnichannel.getQueueWorker()).shouldStart(); }, // eslint-disable-next-line @typescript-eslint/naming-convention @@ -198,7 +191,7 @@ export const RoutingManager: Routing = { logger.debug(`Unassigning current agent for inquiry ${inquiry._id}`); await LivechatRooms.removeAgentByRoomId(rid); await this.removeAllRoomSubscriptions(room); - await dispatchAgentDelegated(rid, null); + await dispatchAgentDelegated(rid); } await dispatchInquiryQueued(inquiry); @@ -252,9 +245,8 @@ export const RoutingManager: Routing = { if (!agent) { logger.debug(`Cannot take Inquiry ${inquiry._id}: Precondition failed for agent`); - const cbRoom = await callbacks.run<'livechat.onAgentAssignmentFailed'>('livechat.onAgentAssignmentFailed', { + const cbRoom = await callbacks.run<'livechat.onAgentAssignmentFailed'>('livechat.onAgentAssignmentFailed', room, { inquiry, - room, options, }); return cbRoom; @@ -309,7 +301,6 @@ export const RoutingManager: Routing = { if (ignoreUser && ignoreUser._id === u._id) { return; } - // @ts-expect-error - File still in JS, expecting error for now on `u` types void removeAgentFromSubscription(roomId, u); }); }, diff --git a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts index a0f456fbe93c..cb7ecbf079f2 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts +++ b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts @@ -1,10 +1,10 @@ -import moment from 'moment'; -import { LivechatRooms, Users, LivechatVisitors, LivechatAgentActivity } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { LivechatRooms, Users, LivechatVisitors, LivechatAgentActivity } from '@rocket.chat/models'; +import moment from 'moment'; +import { secondsToHHMMSS } from '../../../../../lib/utils/secondsToHHMMSS'; import { settings } from '../../../../settings/server'; import { Livechat } from '../Livechat'; -import { secondsToHHMMSS } from '../../../../utils/server'; import { findPercentageOfAbandonedRoomsAsync, findAllAverageOfChatDurationTimeAsync, diff --git a/apps/meteor/app/livechat/server/lib/logger.ts b/apps/meteor/app/livechat/server/lib/logger.ts index ee6b2969c5c5..bb452cd4db04 100644 --- a/apps/meteor/app/livechat/server/lib/logger.ts +++ b/apps/meteor/app/livechat/server/lib/logger.ts @@ -1,4 +1,4 @@ -import { Logger } from '../../../../server/lib/logger/Logger'; +import { Logger } from '@rocket.chat/logger'; export const callbackLogger = new Logger('[Omnichannel] Callback'); export const businessHourLogger = new Logger('Business Hour'); diff --git a/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts b/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts index 1dd665c95b4c..f526280c757b 100644 --- a/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts +++ b/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts @@ -1,9 +1,9 @@ import type { IRoutingMethod, RoutingMethodConfig, SelectedAgent } from '@rocket.chat/core-typings'; import { LivechatDepartmentAgents, Users } from '@rocket.chat/models'; -import { RoutingManager } from '../RoutingManager'; import { callbacks } from '../../../../../lib/callbacks'; import { settings } from '../../../../settings/server'; +import { RoutingManager } from '../RoutingManager'; /* Auto Selection Queuing method: * diff --git a/apps/meteor/app/livechat/server/lib/routing/External.ts b/apps/meteor/app/livechat/server/lib/routing/External.ts index 046055a940c7..b9f9e99e8468 100644 --- a/apps/meteor/app/livechat/server/lib/routing/External.ts +++ b/apps/meteor/app/livechat/server/lib/routing/External.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; import type { IRoutingMethod, RoutingMethodConfig, SelectedAgent } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { Meteor } from 'meteor/meteor'; +import { SystemLogger } from '../../../../../server/lib/logger/system'; import { settings } from '../../../../settings/server'; import { RoutingManager } from '../RoutingManager'; -import { SystemLogger } from '../../../../../server/lib/logger/system'; class ExternalQueue implements IRoutingMethod { config: RoutingMethodConfig; diff --git a/apps/meteor/app/livechat/server/lib/stream/agentStatus.ts b/apps/meteor/app/livechat/server/lib/stream/agentStatus.ts index dc105ef1a00e..bbce5d16efb4 100644 --- a/apps/meteor/app/livechat/server/lib/stream/agentStatus.ts +++ b/apps/meteor/app/livechat/server/lib/stream/agentStatus.ts @@ -1,6 +1,7 @@ -import { Livechat } from '../Livechat'; +import { Logger } from '@rocket.chat/logger'; + import { settings } from '../../../../settings/server'; -import { Logger } from '../../../../logger/server'; +import { Livechat } from '../Livechat'; const logger = new Logger('AgentStatusWatcher'); diff --git a/apps/meteor/app/livechat/server/methods/addAgent.ts b/apps/meteor/app/livechat/server/methods/addAgent.ts index 59d26eb2756f..4c7281d6b830 100644 --- a/apps/meteor/app/livechat/server/methods/addAgent.ts +++ b/apps/meteor/app/livechat/server/methods/addAgent.ts @@ -1,6 +1,6 @@ +import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import type { IUser } from '@rocket.chat/core-typings'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/addManager.ts b/apps/meteor/app/livechat/server/methods/addManager.ts index 47d145ff8042..f3078d8ec73d 100644 --- a/apps/meteor/app/livechat/server/methods/addManager.ts +++ b/apps/meteor/app/livechat/server/methods/addManager.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts b/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts index f045294571be..76392fcdf721 100644 --- a/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts +++ b/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/Livechat'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/closeRoom.ts b/apps/meteor/app/livechat/server/methods/closeRoom.ts index e8c0cba99e86..1374d86ab9f7 100644 --- a/apps/meteor/app/livechat/server/methods/closeRoom.ts +++ b/apps/meteor/app/livechat/server/methods/closeRoom.ts @@ -1,11 +1,11 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { Meteor } from 'meteor/meteor'; import { Users, LivechatRooms, Subscriptions as SubscriptionRaw } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../lib/LivechatTyped'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/LivechatTyped'; type CloseRoomOptions = { clientAction?: boolean; diff --git a/apps/meteor/app/livechat/server/methods/discardTranscript.ts b/apps/meteor/app/livechat/server/methods/discardTranscript.ts index edb1ea51a5b0..d46c8ffea35f 100644 --- a/apps/meteor/app/livechat/server/methods/discardTranscript.ts +++ b/apps/meteor/app/livechat/server/methods/discardTranscript.ts @@ -1,9 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -14,7 +15,11 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async 'livechat:discardTranscript'(rid: string) { + methodDeprecationLogger.method('livechat:discardTranscript', '7.0.0'); check(rid, String); + methodDeprecationLogger.warn( + 'The method "livechat:discardTranscript" is deprecated and will be removed after version v7.0.0. Use "livechat/transcript/:rid" (DELETE) instead.', + ); const user = Meteor.userId(); diff --git a/apps/meteor/app/livechat/server/methods/getAgentData.ts b/apps/meteor/app/livechat/server/methods/getAgentData.ts index 991be42434bf..d32d24d7f7c1 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentData.ts +++ b/apps/meteor/app/livechat/server/methods/getAgentData.ts @@ -1,9 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; +import type { ILivechatAgent } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { ILivechatAgent } from '@rocket.chat/core-typings'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; declare module '@rocket.chat/ui-contexts' { @@ -21,6 +22,10 @@ Meteor.methods({ check(roomId, String); check(token, String); + methodDeprecationLogger.warn( + 'The method "livechat:getAgentData" is deprecated and will be removed after version v7.0.0. Use "livechat/agent.info/:rid/:token" instead.', + ); + const room = await LivechatRooms.findOneById(roomId); const visitor = await LivechatVisitors.getVisitorByToken(token); diff --git a/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts b/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts index 88db9c58d159..94fae239b74c 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts +++ b/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts @@ -1,8 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { @@ -17,6 +18,10 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async 'livechat:getAgentOverviewData'(options) { + methodDeprecationLogger.warn( + 'The method "livechat:getAgentOverviewData" is deprecated and will be removed after version v7.0.0. Use "livechat/analytics/agent-overview" instead.', + ); + const uid = Meteor.userId(); if (!uid || !(await hasPermissionAsync(uid, 'view-livechat-manager'))) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { diff --git a/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts b/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts index 12571bd51081..1250985cd2ef 100644 --- a/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts +++ b/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts @@ -1,6 +1,6 @@ +import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Users } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/Livechat'; diff --git a/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts b/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts index 83669ea53de4..48313f1ce67c 100644 --- a/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts +++ b/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts @@ -1,6 +1,6 @@ +import { Users } from '@rocket.chat/models'; import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Users } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/livechat/server/methods/getCustomFields.ts b/apps/meteor/app/livechat/server/methods/getCustomFields.ts index 00d719c443f8..ec0ac35d6c21 100644 --- a/apps/meteor/app/livechat/server/methods/getCustomFields.ts +++ b/apps/meteor/app/livechat/server/methods/getCustomFields.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import type { ILivechatCustomField } from '@rocket.chat/core-typings'; import { LivechatCustomField } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { ILivechatCustomField } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts b/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts index 812afa01f6b0..197898e8d2b2 100644 --- a/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts +++ b/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { LivechatRooms, Messages } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/getNextAgent.ts b/apps/meteor/app/livechat/server/methods/getNextAgent.ts index 94961a898543..53b8441cad2e 100644 --- a/apps/meteor/app/livechat/server/methods/getNextAgent.ts +++ b/apps/meteor/app/livechat/server/methods/getNextAgent.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { LivechatRooms, Users } from '@rocket.chat/models'; import type { ILivechatAgent } from '@rocket.chat/core-typings'; +import { LivechatRooms, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/LivechatTyped'; +import { callbacks } from '../../../../lib/callbacks'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; -import { callbacks } from '../../../../lib/callbacks'; +import { Livechat } from '../lib/LivechatTyped'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/loadHistory.ts b/apps/meteor/app/livechat/server/methods/loadHistory.ts index 4a2b775ebcf6..8d747cad20d8 100644 --- a/apps/meteor/app/livechat/server/methods/loadHistory.ts +++ b/apps/meteor/app/livechat/server/methods/loadHistory.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import type { IMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IMessage } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { loadMessageHistory } from '../../../lib/server/functions/loadMessageHistory'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/loginByToken.ts b/apps/meteor/app/livechat/server/methods/loginByToken.ts index 135119cf4953..54ba97e89926 100644 --- a/apps/meteor/app/livechat/server/methods/loginByToken.ts +++ b/apps/meteor/app/livechat/server/methods/loginByToken.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { LivechatVisitors } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/pageVisited.ts b/apps/meteor/app/livechat/server/methods/pageVisited.ts index 85184ad4908f..91cfb4afeef8 100644 --- a/apps/meteor/app/livechat/server/methods/pageVisited.ts +++ b/apps/meteor/app/livechat/server/methods/pageVisited.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/registerGuest.ts b/apps/meteor/app/livechat/server/methods/registerGuest.ts index 94eddc2983da..e35b2693400d 100644 --- a/apps/meteor/app/livechat/server/methods/registerGuest.ts +++ b/apps/meteor/app/livechat/server/methods/registerGuest.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; +import type { ILivechatVisitor, IRoom } from '@rocket.chat/core-typings'; import { LivechatVisitors, Messages, LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { ILivechatVisitor, IRoom } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; +import { callbacks } from '../../../../lib/callbacks'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/Livechat'; import { Livechat as LivechatTyped } from '../lib/LivechatTyped'; -import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -import { callbacks } from '../../../../lib/callbacks'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/removeAgent.ts b/apps/meteor/app/livechat/server/methods/removeAgent.ts index feb73aa0ed4b..e49c3a21b8e2 100644 --- a/apps/meteor/app/livechat/server/methods/removeAgent.ts +++ b/apps/meteor/app/livechat/server/methods/removeAgent.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts b/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts index c6e4956369f1..149cd0a5c5c9 100644 --- a/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts +++ b/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts @@ -1,11 +1,11 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { LivechatRooms } from '@rocket.chat/models'; +import { callbacks } from '../../../../lib/callbacks'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; -import { callbacks } from '../../../../lib/callbacks'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/removeCustomField.ts b/apps/meteor/app/livechat/server/methods/removeCustomField.ts index 7d828a6f4568..1d3e7c817738 100644 --- a/apps/meteor/app/livechat/server/methods/removeCustomField.ts +++ b/apps/meteor/app/livechat/server/methods/removeCustomField.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { LivechatCustomField } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/livechat/server/methods/removeDepartment.ts b/apps/meteor/app/livechat/server/methods/removeDepartment.ts index 56e2e723dd57..3c46f3a424d6 100644 --- a/apps/meteor/app/livechat/server/methods/removeDepartment.ts +++ b/apps/meteor/app/livechat/server/methods/removeDepartment.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/livechat/server/methods/removeManager.ts b/apps/meteor/app/livechat/server/methods/removeManager.ts index 8e23f861b76d..758bf8130e7f 100644 --- a/apps/meteor/app/livechat/server/methods/removeManager.ts +++ b/apps/meteor/app/livechat/server/methods/removeManager.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/removeRoom.ts b/apps/meteor/app/livechat/server/methods/removeRoom.ts index 173f302cba32..db3fcf2c849f 100644 --- a/apps/meteor/app/livechat/server/methods/removeRoom.ts +++ b/apps/meteor/app/livechat/server/methods/removeRoom.ts @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import { LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { LivechatRooms } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; diff --git a/apps/meteor/app/livechat/server/methods/removeTrigger.ts b/apps/meteor/app/livechat/server/methods/removeTrigger.ts index 4165283af20c..c403dcd3edac 100644 --- a/apps/meteor/app/livechat/server/methods/removeTrigger.ts +++ b/apps/meteor/app/livechat/server/methods/removeTrigger.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { LivechatTrigger } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/livechat/server/methods/requestTranscript.ts b/apps/meteor/app/livechat/server/methods/requestTranscript.ts index ab4d74c8d554..02ac65a48173 100644 --- a/apps/meteor/app/livechat/server/methods/requestTranscript.ts +++ b/apps/meteor/app/livechat/server/methods/requestTranscript.ts @@ -1,10 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/Livechat'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -15,6 +16,7 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async 'livechat:requestTranscript'(rid, email, subject) { + methodDeprecationLogger.method('livechat:requestTranscript', '7.0.0'); check(rid, String); check(email, String); diff --git a/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts b/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts index 436d234af524..57a2b0afa3d5 100644 --- a/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts +++ b/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts @@ -1,7 +1,7 @@ import type { ILivechatDepartment, IRoom } from '@rocket.chat/core-typings'; +import { LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { LivechatRooms } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/Livechat'; diff --git a/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts b/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts index 52872faf5050..90d9815d4cf1 100644 --- a/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts +++ b/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { Livechat } from '../lib/LivechatTyped'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/livechat/server/methods/saveAppearance.ts b/apps/meteor/app/livechat/server/methods/saveAppearance.ts index 50c83923d7bd..35152d136afd 100644 --- a/apps/meteor/app/livechat/server/methods/saveAppearance.ts +++ b/apps/meteor/app/livechat/server/methods/saveAppearance.ts @@ -1,8 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import { Settings } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -13,6 +14,7 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async 'livechat:saveAppearance'(settings) { + methodDeprecationLogger.method('livechat:saveAppearance', '7.0.0'); const uid = Meteor.userId(); if (!uid || !(await hasPermissionAsync(uid, 'view-livechat-manager'))) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { diff --git a/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts b/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts index 149ac645efcb..2e3e64365649 100644 --- a/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts +++ b/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { businessHourManager } from '../business-hour'; diff --git a/apps/meteor/app/livechat/server/methods/saveCustomField.ts b/apps/meteor/app/livechat/server/methods/saveCustomField.ts index 81c563c67a78..8a0435560dd5 100644 --- a/apps/meteor/app/livechat/server/methods/saveCustomField.ts +++ b/apps/meteor/app/livechat/server/methods/saveCustomField.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { LivechatCustomField } from '@rocket.chat/models'; import type { ILivechatCustomField } from '@rocket.chat/core-typings'; +import { LivechatCustomField } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/livechat/server/methods/saveDepartment.ts b/apps/meteor/app/livechat/server/methods/saveDepartment.ts index 030e665a1b84..dd83a294cb0e 100644 --- a/apps/meteor/app/livechat/server/methods/saveDepartment.ts +++ b/apps/meteor/app/livechat/server/methods/saveDepartment.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ILivechatDepartment } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { LivechatEnterprise } from '../../../../ee/app/livechat-enterprise/server/lib/LivechatEnterprise'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts b/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts index 7d8a9ba997cd..edc15f940322 100644 --- a/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts +++ b/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/saveInfo.ts b/apps/meteor/app/livechat/server/methods/saveInfo.ts index d9e6101977c1..66674376407b 100644 --- a/apps/meteor/app/livechat/server/methods/saveInfo.ts +++ b/apps/meteor/app/livechat/server/methods/saveInfo.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { LivechatRooms, Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { callbacks } from '../../../../lib/callbacks'; -import { Livechat } from '../lib/Livechat'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/saveIntegration.ts b/apps/meteor/app/livechat/server/methods/saveIntegration.ts index 474e7972b99e..18bad34f0aea 100644 --- a/apps/meteor/app/livechat/server/methods/saveIntegration.ts +++ b/apps/meteor/app/livechat/server/methods/saveIntegration.ts @@ -2,8 +2,8 @@ import { Settings } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { trim } from '../../../../lib/utils/stringUtils'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts index 92c3cd855801..48dea8175ea8 100644 --- a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts +++ b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import _ from 'underscore'; import { LivechatRooms, LivechatVisitors } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import type { UpdateResult } from 'mongodb'; +import _ from 'underscore'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/saveTrigger.ts b/apps/meteor/app/livechat/server/methods/saveTrigger.ts index 6addcb715ef7..37f78d081203 100644 --- a/apps/meteor/app/livechat/server/methods/saveTrigger.ts +++ b/apps/meteor/app/livechat/server/methods/saveTrigger.ts @@ -1,10 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; +import type { ILivechatTrigger } from '@rocket.chat/core-typings'; import { LivechatTrigger } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { ILivechatTrigger } from '@rocket.chat/core-typings'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -15,6 +16,7 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async 'livechat:saveTrigger'(trigger) { + methodDeprecationLogger.method('livechat:saveTrigger', '7.0.0'); const uid = Meteor.userId(); if (!uid || !(await hasPermissionAsync(uid, 'view-livechat-manager'))) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { diff --git a/apps/meteor/app/livechat/server/methods/searchAgent.ts b/apps/meteor/app/livechat/server/methods/searchAgent.ts index 5de12a11b5aa..2a69679f2a07 100644 --- a/apps/meteor/app/livechat/server/methods/searchAgent.ts +++ b/apps/meteor/app/livechat/server/methods/searchAgent.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts index 6d1aa0867bca..7d64763cd634 100644 --- a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts +++ b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts @@ -1,8 +1,3 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { Random } from '@rocket.chat/random'; -import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { MessageAttachment, ImageAttachmentProps, @@ -10,6 +5,11 @@ import type { VideoAttachmentProps, IUpload, } from '@rocket.chat/core-typings'; +import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { FileUpload } from '../../../file-upload/server'; import { sendMessageLivechat } from './sendMessageLivechat'; diff --git a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts index 725073000b2f..c7d412ea4a06 100644 --- a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts +++ b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import type { MessageAttachment } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/Livechat'; import { settings } from '../../../settings/server'; +import { Livechat } from '../lib/Livechat'; interface ILivechatMessage { token: string; diff --git a/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts b/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts index 86767255df46..9a475de5e32d 100644 --- a/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts +++ b/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts @@ -1,15 +1,15 @@ -import { Meteor } from 'meteor/meteor'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - 'livechat:sendOfflineMessage'(data: { name: string; email: string; message: string }): Promise; + 'livechat:sendOfflineMessage'(data: { name: string; email: string; message: string }): Promise; } } @@ -23,7 +23,7 @@ Meteor.methods({ message: String, }); - return Livechat.sendOfflineMessage(data); + await Livechat.sendOfflineMessage(data); }, }); diff --git a/apps/meteor/app/livechat/server/methods/sendTranscript.ts b/apps/meteor/app/livechat/server/methods/sendTranscript.ts index fc41c38d77fb..7456397de7b4 100644 --- a/apps/meteor/app/livechat/server/methods/sendTranscript.ts +++ b/apps/meteor/app/livechat/server/methods/sendTranscript.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; +import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; diff --git a/apps/meteor/app/livechat/server/methods/setCustomField.ts b/apps/meteor/app/livechat/server/methods/setCustomField.ts index d8127e6c9bc5..fb07c2b29a4e 100644 --- a/apps/meteor/app/livechat/server/methods/setCustomField.ts +++ b/apps/meteor/app/livechat/server/methods/setCustomField.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; import { LivechatVisitors, LivechatCustomField, LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import type { UpdateResult, Document } from 'mongodb'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts index dd7cf8a6e231..61e6b21267da 100644 --- a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts +++ b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import { LivechatVisitors, Messages, LivechatRooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { Livechat } from '../lib/Livechat'; -import { normalizeTransferredByData } from '../lib/Helper'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { normalizeTransferredByData } from '../lib/Helper'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/setUpConnection.ts b/apps/meteor/app/livechat/server/methods/setUpConnection.ts index 40df523b378a..658f6249c9b1 100644 --- a/apps/meteor/app/livechat/server/methods/setUpConnection.ts +++ b/apps/meteor/app/livechat/server/methods/setUpConnection.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { Livechat } from '../lib/Livechat'; diff --git a/apps/meteor/app/livechat/server/methods/takeInquiry.ts b/apps/meteor/app/livechat/server/methods/takeInquiry.ts index 1bf84347b0ff..17007d7da8c2 100644 --- a/apps/meteor/app/livechat/server/methods/takeInquiry.ts +++ b/apps/meteor/app/livechat/server/methods/takeInquiry.ts @@ -1,10 +1,10 @@ +import { LivechatInquiry, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { LivechatInquiry, Users } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { RoutingManager } from '../lib/RoutingManager'; import { settings } from '../../../settings/server'; +import { RoutingManager } from '../lib/RoutingManager'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/livechat/server/methods/transfer.ts b/apps/meteor/app/livechat/server/methods/transfer.ts index b60cb1c318ad..2dc796fc6c94 100644 --- a/apps/meteor/app/livechat/server/methods/transfer.ts +++ b/apps/meteor/app/livechat/server/methods/transfer.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; +import type { IUser } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, Subscriptions, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IUser } from '@rocket.chat/core-typings'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { Livechat } from '../lib/Livechat'; -import { normalizeTransferredByData } from '../lib/Helper'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { normalizeTransferredByData } from '../lib/Helper'; +import { Livechat } from '../lib/Livechat'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -60,6 +60,12 @@ Meteor.methods({ const guest = await LivechatVisitors.findOneById(room.v?._id); + const user = await Meteor.userAsync(); + + if (!user) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'livechat:transfer' }); + } + const normalizedTransferData: { roomId: string; userId?: string; @@ -70,7 +76,7 @@ Meteor.methods({ transferredTo?: Pick; } = { ...transferData, - transferredBy: normalizeTransferredByData((await Meteor.userAsync()) || {}, room), + transferredBy: normalizeTransferredByData(user, room), }; if (normalizedTransferData.userId) { diff --git a/apps/meteor/app/livechat/server/methods/webhookTest.ts b/apps/meteor/app/livechat/server/methods/webhookTest.ts index d897187e1e40..12a4711d75b4 100644 --- a/apps/meteor/app/livechat/server/methods/webhookTest.ts +++ b/apps/meteor/app/livechat/server/methods/webhookTest.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { settings } from '../../../settings/server'; const postCatchError = async function (url: string, options?: Record | undefined) { try { diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts index 5da49ccd1994..d5ef83272550 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts @@ -1,8 +1,8 @@ -import type { IUser, ILivechatDepartment, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatDepartmentAgents, LivechatInquiry, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; -import { hasRoleAsync } from '../../authorization/server/functions/hasRole'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; +import { hasRoleAsync } from '../../authorization/server/functions/hasRole'; import { RoutingManager } from './lib/RoutingManager'; type OmnichannelRoomAccessValidator = ( @@ -47,10 +47,10 @@ export const validators: OmnichannelRoomAccessValidator[] = [ let departmentIds; if (!(await hasRoleAsync(user._id, 'livechat-manager'))) { - const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id).toArray()).map((d) => d.departmentId); - departmentIds = (await LivechatDepartment.find({ _id: { $in: departmentAgents }, enabled: true }).toArray()).map( - (d: ILivechatDepartment) => d._id, + const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id, { projection: { departmentId: 1 } }).toArray()).map( + (d) => d.departmentId, ); + departmentIds = (await LivechatDepartment.findEnabledInIds(departmentAgents, { projection: { _id: 1 } }).toArray()).map((d) => d._id); } const filter = { @@ -75,7 +75,9 @@ export const validators: OmnichannelRoomAccessValidator[] = [ if (!room.departmentId || room.open || !user?._id) { return; } - const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId); + const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId, { + projection: { _id: 1 }, + }); if (!agentOfDepartment) { return; } diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts b/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts index bac9cb5bd7a3..a9b29bac2e8f 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts @@ -1,6 +1,6 @@ -import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings'; import type { IAuthorizationLivechat } from '@rocket.chat/core-services'; import { ServiceClassInternal } from '@rocket.chat/core-services'; +import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { validators } from './roomAccessValidator.compatibility'; diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.ts b/apps/meteor/app/livechat/server/sendMessageBySMS.ts index 8449c2c7bacf..ea220b24d149 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.ts +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.ts @@ -1,6 +1,6 @@ +import { OmnichannelIntegration } from '@rocket.chat/core-services'; import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; -import { OmnichannelIntegration } from '@rocket.chat/core-services'; import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; @@ -9,7 +9,7 @@ import { callbackLogger } from './lib/logger'; callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { callbackLogger.debug('Attempting to send SMS message'); // skips this callback if the message was edited if (isEditedMessage(message)) { @@ -40,11 +40,12 @@ callbacks.add( return message; } - let extraData = {}; + const { rid, u: { _id: userId } = {} } = message; + let extraData = { rid, userId }; if (message.file) { message = { ...(await normalizeMessageFileUpload(message)), ...{ _updatedAt: message._updatedAt } }; - const { fileUpload, rid, u: { _id: userId } = {} } = message; - extraData = Object.assign({}, { rid, userId, fileUpload }); + const { fileUpload } = message; + extraData = Object.assign({}, extraData, { fileUpload }); } if (message.location) { diff --git a/apps/meteor/app/livechat/server/startup.ts b/apps/meteor/app/livechat/server/startup.ts index d8c5ba098d65..f24f88975b22 100644 --- a/apps/meteor/app/livechat/server/startup.ts +++ b/apps/meteor/app/livechat/server/startup.ts @@ -1,29 +1,29 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; import type { IUser } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; import { callbacks } from '../../../lib/callbacks'; +import { beforeLeaveRoomCallback } from '../../../lib/callbacks/beforeLeaveRoomCallback'; +import { i18n } from '../../../server/lib/i18n'; +import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; +import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { settings } from '../../settings/server'; -import { LivechatAgentActivityMonitor } from './statistics/LivechatAgentActivityMonitor'; import { businessHourManager } from './business-hour'; import { createDefaultBusinessHourIfNotExists } from './business-hour/Helper'; -import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { Livechat } from './lib/Livechat'; import { RoutingManager } from './lib/RoutingManager'; +import { LivechatAgentActivityMonitor } from './statistics/LivechatAgentActivityMonitor'; import './roomAccessValidator.internalService'; -import { i18n } from '../../../server/lib/i18n'; Meteor.startup(async () => { roomCoordinator.setRoomFind('l', (_id) => LivechatRooms.findOneById(_id)); - callbacks.add( - 'beforeLeaveRoom', - function (user, room) { + beforeLeaveRoomCallback.add( + (user, room) => { if (!isOmnichannelRoom(room)) { - return user; + return; } throw new Meteor.Error( i18n.t('You_cant_leave_a_livechat_room_Please_use_the_close_button', { @@ -37,7 +37,7 @@ Meteor.startup(async () => { callbacks.add( 'beforeJoinRoom', - async function (user, room) { + async (user, room) => { if (isOmnichannelRoom(room) && !(await hasPermissionAsync(user._id, 'view-l-room'))) { throw new Meteor.Error('error-user-is-not-agent', 'User is not an Omnichannel Agent', { method: 'beforeJoinRoom', @@ -72,8 +72,8 @@ Meteor.startup(async () => { Livechat.logger.debug(`Business hour manager stopped`); }); - settings.watch('Livechat_Routing_Method', function (value) { - RoutingManager.setMethodNameAndStartQueue(value); + settings.watch('Livechat_Routing_Method', (value) => { + void RoutingManager.setMethodNameAndStartQueue(value); }); // Remove when accounts.onLogout is async diff --git a/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts b/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts index e861eae543ca..76e9e04f1a24 100644 --- a/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts +++ b/apps/meteor/app/livechat/server/statistics/LivechatAgentActivityMonitor.ts @@ -1,8 +1,8 @@ -import moment from 'moment'; import type { ILivechatAgent, ISocketConnection } from '@rocket.chat/core-typings'; -import { Meteor } from 'meteor/meteor'; -import { LivechatAgentActivity, Sessions, Users } from '@rocket.chat/models'; import { cronJobs } from '@rocket.chat/cron'; +import { LivechatAgentActivity, Sessions, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; import { callbacks } from '../../../../lib/callbacks'; diff --git a/apps/meteor/app/logger/server/index.ts b/apps/meteor/app/logger/server/index.ts deleted file mode 100644 index 5630b3a0aa2a..000000000000 --- a/apps/meteor/app/logger/server/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// TODO there are imports pointing to this file still, ideally we should point everything to "/server/lib/logger/Logger" and remove this file -export { Logger } from '../../../server/lib/logger/Logger'; diff --git a/apps/meteor/app/mail-messages/server/functions/sendMail.ts b/apps/meteor/app/mail-messages/server/functions/sendMail.ts index 881b5eb7f706..ce57fc06b1c8 100644 --- a/apps/meteor/app/mail-messages/server/functions/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/functions/sendMail.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import EJSON from 'ejson'; -import { escapeHTML } from '@rocket.chat/string-helpers'; -import type { Filter } from 'mongodb'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import EJSON from 'ejson'; +import { Meteor } from 'meteor/meteor'; +import type { Filter } from 'mongodb'; -import { placeholders } from '../../../utils/server'; +import { generatePath } from '../../../../lib/utils/generatePath'; import { SystemLogger } from '../../../../server/lib/logger/system'; import * as Mailer from '../../../mailer/server/api'; -import { generatePath } from '../../../../lib/utils/generatePath'; +import { placeholders } from '../../../utils/server/placeholders'; export const sendMail = async function ({ from, diff --git a/apps/meteor/app/mail-messages/server/methods/sendMail.ts b/apps/meteor/app/mail-messages/server/methods/sendMail.ts index ebb781dbad8c..158d271d5379 100644 --- a/apps/meteor/app/mail-messages/server/methods/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/methods/sendMail.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { Mailer } from '../lib/Mailer'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Mailer } from '../lib/Mailer'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts b/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts index 46296078e08e..24243cc98c2d 100644 --- a/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts +++ b/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; +import { Meteor } from 'meteor/meteor'; -import { Mailer } from '../lib/Mailer'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +import { Mailer } from '../lib/Mailer'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/mailer/server/api.ts b/apps/meteor/app/mailer/server/api.ts index 868ee8c6fcc4..b50fdfd26a2a 100644 --- a/apps/meteor/app/mailer/server/api.ts +++ b/apps/meteor/app/mailer/server/api.ts @@ -1,18 +1,18 @@ -import { Meteor } from 'meteor/meteor'; -import { Email } from 'meteor/email'; -import _ from 'underscore'; -import juice from 'juice'; -import stripHtml from 'string-strip-html'; -import { escapeHTML } from '@rocket.chat/string-helpers'; import type { ISetting } from '@rocket.chat/core-typings'; import { Settings } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import juice from 'juice'; +import { Email } from 'meteor/email'; +import { Meteor } from 'meteor/meteor'; +import stripHtml from 'string-strip-html'; +import _ from 'underscore'; -import { settings } from '../../settings/server'; -import { replaceVariables } from './replaceVariables'; import { Apps } from '../../../ee/server/apps'; import { validateEmail } from '../../../lib/emailValidator'; import { strLeft, strRightBack } from '../../../lib/utils/stringUtils'; import { i18n } from '../../../server/lib/i18n'; +import { settings } from '../../settings/server'; +import { replaceVariables } from './replaceVariables'; let contentHeader: string | undefined; let contentFooter: string | undefined; @@ -75,11 +75,12 @@ export const wrap = (html: string, data: { [key: string]: unknown } = {}): strin } if (!body) { - throw new Error('`body` is not set yet'); + throw new Error('error-email-body-not-initialized'); } return replaceEscaped(body.replace('{{body}}', html), data); }; + export const inlinecss = (html: string): string => { const css = settings.get('email_style'); return css ? juice.inlineContent(html, css) : html; diff --git a/apps/meteor/app/markdown/lib/hljs.js b/apps/meteor/app/markdown/lib/hljs.js index 58c486ac3b86..2ddfc675646d 100644 --- a/apps/meteor/app/markdown/lib/hljs.js +++ b/apps/meteor/app/markdown/lib/hljs.js @@ -1,12 +1,12 @@ import hljs from 'hljs9/lib/highlight'; import clean from 'hljs9/lib/languages/clean'; -import markdown from 'hljs9/lib/languages/markdown'; import javascript from 'hljs9/lib/languages/javascript'; +import markdown from 'hljs9/lib/languages/markdown'; hljs.registerLanguage('markdown', markdown); hljs.registerLanguage('clean', clean); hljs.registerLanguage('javascript', javascript); -// eslint-disable-next-line complexity + export const register = async (lang) => { switch (lang) { case 'onec': diff --git a/apps/meteor/app/markdown/lib/markdown.js b/apps/meteor/app/markdown/lib/markdown.js index 8a8c67f7ef72..3c3acdb17893 100644 --- a/apps/meteor/app/markdown/lib/markdown.js +++ b/apps/meteor/app/markdown/lib/markdown.js @@ -2,12 +2,12 @@ * Markdown is a named function that will parse markdown syntax * @param {Object} message - The message object */ -import { Meteor } from 'meteor/meteor'; import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; -import { original } from './parser/original/original'; import { filtered } from './parser/filtered/filtered'; import { code } from './parser/original/code'; +import { original } from './parser/original/original'; const parsers = { original, diff --git a/apps/meteor/app/markdown/lib/parser/original/original.js b/apps/meteor/app/markdown/lib/parser/original/original.js index ee951d2a5d2d..c3a7ba160b27 100644 --- a/apps/meteor/app/markdown/lib/parser/original/original.js +++ b/apps/meteor/app/markdown/lib/parser/original/original.js @@ -2,8 +2,8 @@ * Markdown is a named function that will parse markdown syntax * @param {Object} message - The message object */ -import { markdown } from './markdown.js'; import { code } from './code.js'; +import { markdown } from './markdown.js'; export const original = ( message, diff --git a/apps/meteor/app/markdown/lib/parser/original/token.ts b/apps/meteor/app/markdown/lib/parser/original/token.ts index 8abb9c45be16..d4b5a4ef8ace 100644 --- a/apps/meteor/app/markdown/lib/parser/original/token.ts +++ b/apps/meteor/app/markdown/lib/parser/original/token.ts @@ -2,8 +2,8 @@ * Markdown is a named function that will parse markdown syntax * @param {String} msg - The message html */ -import { Random } from '@rocket.chat/random'; import type { IMessage, TokenType, TokenExtra } from '@rocket.chat/core-typings'; +import { Random } from '@rocket.chat/random'; export const addAsToken = (message: IMessage, html: string, type: TokenType, extra?: TokenExtra): string => { if (!message.tokens) { diff --git a/apps/meteor/app/mentions/lib/MentionsParser.js b/apps/meteor/app/mentions/lib/MentionsParser.js index 609bf34855a4..87329ac9f120 100644 --- a/apps/meteor/app/mentions/lib/MentionsParser.js +++ b/apps/meteor/app/mentions/lib/MentionsParser.js @@ -38,7 +38,7 @@ export class MentionsParser { } get userMentionRegex() { - return new RegExp(`(^|\\s|> ?)@(${this.pattern}(@(${this.pattern}))?(:([0-9a-zA-Z-_.]+))?)`, 'gm'); + return new RegExp(`(^|\\s|>)@(${this.pattern}(@(${this.pattern}))?(:([0-9a-zA-Z-_.]+))?)`, 'gm'); } get channelMentionRegex() { @@ -97,7 +97,7 @@ export class MentionsParser { !temp && !( channels && - channels.find(function (c) { + channels.find((c) => { return c.dname ? c.dname === mention : c.name === mention; }) ) @@ -107,7 +107,7 @@ export class MentionsParser { const channel = channels && - channels.find(function ({ name, dname }) { + channels.find(({ name, dname }) => { return dname ? dname === mention : name === mention; }); const reference = channel ? channel._id : mention; diff --git a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts new file mode 100644 index 000000000000..b1355c90cb93 --- /dev/null +++ b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts @@ -0,0 +1,35 @@ +import { Team } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; + +import { callbacks } from '../../../lib/callbacks'; +import { settings } from '../../settings/server'; + +interface IExtraDataForNotification { + userMentions: any[]; + otherMentions: any[]; + message: IMessage; +} + +const beforeGetMentions = async (mentionIds: string[], extra?: IExtraDataForNotification) => { + const { otherMentions } = extra ?? {}; + + const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); + + if (!teamIds?.length) { + return mentionIds; + } + + const members = await Team.getMembersByTeamIds(teamIds, { projection: { userId: 1 } }); + mentionIds.push(...new Set(members.map(({ userId }) => userId).filter((userId) => !mentionIds.includes(userId)))); + + return mentionIds; +}; + +settings.watch('Troubleshoot_Disable_Teams_Mention', (value) => { + if (value) { + callbacks.remove('beforeGetMentions', 'before-get-mentions-get-teams'); + return; + } + + callbacks.add('beforeGetMentions', beforeGetMentions, callbacks.priority.MEDIUM, 'before-get-mentions-get-teams'); +}); diff --git a/apps/meteor/app/mentions/server/index.ts b/apps/meteor/app/mentions/server/index.ts index 474d41a439e1..a04af05b9db1 100644 --- a/apps/meteor/app/mentions/server/index.ts +++ b/apps/meteor/app/mentions/server/index.ts @@ -1,2 +1,3 @@ -import './server'; +import './getMentionedTeamMembers'; import './methods/getUserMentionsByChannel'; +import './server'; diff --git a/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts b/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts index b9b0cf06791f..948187d089ae 100644 --- a/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts +++ b/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; +import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Users, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IMessage } from '@rocket.chat/core-typings'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { canAccessRoomAsync } from '../../../authorization/server'; diff --git a/apps/meteor/app/mentions/server/server.ts b/apps/meteor/app/mentions/server/server.ts index ee9b4470cb2c..13765e99d856 100644 --- a/apps/meteor/app/mentions/server/server.ts +++ b/apps/meteor/app/mentions/server/server.ts @@ -1,24 +1,40 @@ -import { Meteor } from 'meteor/meteor'; -import { api } from '@rocket.chat/core-services'; +import { api, Team } from '@rocket.chat/core-services'; +import type { IUser, IRoom, ITeam } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; -import type { IUser, IRoom } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; -import MentionsServer from './Mentions'; -import { settings } from '../../settings/server'; import { callbacks } from '../../../lib/callbacks'; import { i18n } from '../../../server/lib/i18n'; +import { settings } from '../../settings/server'; +import MentionsServer from './Mentions'; export class MentionQueries { - async getUsers(usernames: string[]): Promise<(Pick & { type: 'user' })[]> { + async getUsers( + usernames: string[], + ): Promise<((Pick & { type: 'user' }) | (Pick & { type: 'team' }))[]> { + const uniqueUsernames = [...new Set(usernames)]; + const teams = await Team.listByNames(uniqueUsernames, { projection: { name: 1 } }); + const users = await Users.find( - { username: { $in: [...new Set(usernames)] } }, + { username: { $in: uniqueUsernames } }, { projection: { _id: true, username: true, name: 1 } }, ).toArray(); - return users.map((user) => ({ + const taggedUsers = users.map((user) => ({ ...user, - type: 'user', + type: 'user' as const, })); + + if (settings.get('Troubleshoot_Disable_Teams_Mention')) { + return taggedUsers; + } + + const taggedTeams = teams.map((team) => ({ + ...team, + type: 'team' as const, + })); + + return [...taggedUsers, ...taggedTeams]; } async getUser(userId: string): Promise { diff --git a/apps/meteor/app/message-mark-as-unread/client/actionButton.ts b/apps/meteor/app/message-mark-as-unread/client/actionButton.ts index 070d1ea750a4..fc4a9d80c43c 100644 --- a/apps/meteor/app/message-mark-as-unread/client/actionButton.ts +++ b/apps/meteor/app/message-mark-as-unread/client/actionButton.ts @@ -1,12 +1,12 @@ import { Meteor } from 'meteor/meteor'; -import { LegacyRoomManager, MessageAction } from '../../ui-utils/client'; -import { messageArgs } from '../../../client/lib/utils/messageArgs'; -import { ChatSubscription } from '../../models/client'; import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; import { dispatchToastMessage } from '../../../client/lib/toast'; -import { sdk } from '../../utils/client/lib/SDKClient'; +import { messageArgs } from '../../../client/lib/utils/messageArgs'; import { router } from '../../../client/providers/RouterProvider'; +import { ChatSubscription } from '../../models/client'; +import { LegacyRoomManager, MessageAction } from '../../ui-utils/client'; +import { sdk } from '../../utils/client/lib/SDKClient'; Meteor.startup(() => { MessageAction.addButton({ @@ -14,6 +14,7 @@ Meteor.startup(() => { icon: 'flag', label: 'Mark_unread', context: ['message', 'message-mobile', 'threads'], + type: 'interaction', async action(_, props) { const { message = messageArgs(this).msg } = props; @@ -44,7 +45,7 @@ Meteor.startup(() => { return message.u._id !== user._id; }, - order: 10, + order: 4, group: 'menu', }); }); diff --git a/apps/meteor/app/message-mark-as-unread/server/logger.ts b/apps/meteor/app/message-mark-as-unread/server/logger.ts index dc95ec0320d8..23a21f529cdd 100644 --- a/apps/meteor/app/message-mark-as-unread/server/logger.ts +++ b/apps/meteor/app/message-mark-as-unread/server/logger.ts @@ -1,4 +1,4 @@ -import { Logger } from '../../logger/server'; +import { Logger } from '@rocket.chat/logger'; const logger = new Logger('MessageMarkAsUnread'); export default logger; diff --git a/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts b/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts index 1f356d3e5a98..2ba0224a0c70 100644 --- a/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts +++ b/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; +import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { Messages, Subscriptions } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IMessage, IRoom } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import logger from './logger'; diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index 2b7419741474..a36883abb0a5 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -1,19 +1,19 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import type { IMessage, IUser, MessageAttachment, MessageQuoteAttachment } from '@rocket.chat/core-typings'; -import { isQuoteAttachment } from '@rocket.chat/core-typings'; import { Message } from '@rocket.chat/core-services'; -import { Messages, Rooms, Subscriptions, Users } from '@rocket.chat/models'; +import { isQuoteAttachment } from '@rocket.chat/core-typings'; +import type { IMessage, IUser, MessageAttachment, MessageQuoteAttachment } from '@rocket.chat/core-typings'; +import { Messages, Rooms, Subscriptions, Users, ReadReceipts } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; +import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../lib/callbacks'; -import { isTheLastMessage } from '../../lib/server'; -import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; +import { isTruthy } from '../../../lib/isTruthy'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; -import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; -import { isTruthy } from '../../../lib/isTruthy'; +import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; +import { settings } from '../../settings/server'; +import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; const recursiveRemove = (msg: MessageAttachment, deep = 1) => { if (!msg || !isQuoteAttachment(msg)) { @@ -113,6 +113,9 @@ Meteor.methods({ originalMessage = await callbacks.run('beforeSaveMessage', originalMessage); await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); + if (settings.get('Message_Read_Receipt_Store_Users')) { + await ReadReceipts.setPinnedByMessageId(message._id, originalMessage.pinned); + } if (isTheLastMessage(room, message)) { await Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); } @@ -219,6 +222,9 @@ Meteor.methods({ await Apps.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); + if (settings.get('Message_Read_Receipt_Store_Users')) { + await ReadReceipts.setPinnedByMessageId(originalMessage._id, originalMessage.pinned); + } return true; }, diff --git a/apps/meteor/app/message-star/server/starMessage.ts b/apps/meteor/app/message-star/server/starMessage.ts index 61317f9a5ab9..006951ad0b72 100644 --- a/apps/meteor/app/message-star/server/starMessage.ts +++ b/apps/meteor/app/message-star/server/starMessage.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; -import { isTheLastMessage } from '../../lib/server'; -import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; +import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; +import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; +import { settings } from '../../settings/server'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/meteor-accounts-saml/client/saml_client.js b/apps/meteor/app/meteor-accounts-saml/client/saml_client.js index 7c3291380ba6..f1f14be530dd 100644 --- a/apps/meteor/app/meteor-accounts-saml/client/saml_client.js +++ b/apps/meteor/app/meteor-accounts-saml/client/saml_client.js @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; import { Random } from '@rocket.chat/random'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; import { sdk } from '../../utils/client/lib/SDKClient'; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts index 1d31b2d4e78c..06c3014a8a56 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts @@ -1,22 +1,25 @@ import type { ServerResponse } from 'http'; -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; -import { Accounts } from 'meteor/accounts-base'; -import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; import type { IUser, IIncomingMessage, IPersonalAccessToken } from '@rocket.chat/core-typings'; import { CredentialTokens, Rooms, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; +import { ensureArray } from '../../../../lib/utils/arrayUtils'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../lib/server/functions/createRoom'; +import { generateUsernameSuggestion } from '../../../lib/server/functions/getUsernameSuggestion'; +import { saveUserIdentity } from '../../../lib/server/functions/saveUserIdentity'; import { settings } from '../../../settings/server'; -import { saveUserIdentity, createRoom, generateUsernameSuggestion, addUserToRoom } from '../../../lib/server/functions'; -import { SAMLServiceProvider } from './ServiceProvider'; -import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; +import { i18n } from '../../../utils/lib/i18n'; import type { ISAMLAction } from '../definition/ISAMLAction'; import type { ISAMLUser } from '../definition/ISAMLUser'; +import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; +import { SAMLServiceProvider } from './ServiceProvider'; import { SAMLUtils } from './Utils'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { ensureArray } from '../../../../lib/utils/arrayUtils'; -import { i18n } from '../../../utils/lib/i18n'; const showErrorMessage = function (res: ServerResponse, err: string): void { res.writeHead(200, { @@ -120,6 +123,7 @@ export class SAML { })); let { username } = userObject; + const { fullName } = userObject; const active = !settings.get('Accounts_ManuallyApproveNewUsers'); @@ -128,7 +132,7 @@ export class SAML { const roles = userObject.roles?.length ? userObject.roles : ensureArray(defaultUserRole.split(',')); const newUser: Record = { - name: userObject.fullName, + name: fullName, active, globalRoles: roles, emails, @@ -196,7 +200,7 @@ export class SAML { // Overwrite fullname if needed if (nameOverwrite === true) { - updateData.name = userObject.fullName; + updateData.name = fullName; } // When updating an user, we only update the roles if we received them from the mapping @@ -217,8 +221,8 @@ export class SAML { }, ); - if (username && username !== user.username) { - await saveUserIdentity({ _id: user._id, username }); + if ((username && username !== user.username) || (fullName && fullName !== user.name)) { + await saveUserIdentity({ _id: user._id, name: fullName || undefined, username }); } // sending token along with the userId diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts index a8094e63fb2f..bf3d2151ce0d 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/ServiceProvider.ts @@ -1,10 +1,14 @@ -import zlib from 'zlib'; import crypto from 'crypto'; import querystring from 'querystring'; import util from 'util'; +import zlib from 'zlib'; import { Meteor } from 'meteor/meteor'; +import type { ILogoutResponse } from '../definition/ILogoutResponse'; +import type { ISAMLRequest } from '../definition/ISAMLRequest'; +import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; +import type { ILogoutRequestValidateCallback, ILogoutResponseValidateCallback, IResponseValidateCallback } from '../definition/callbacks'; import { SAMLUtils } from './Utils'; import { AuthorizeRequest } from './generators/AuthorizeRequest'; import { LogoutRequest } from './generators/LogoutRequest'; @@ -13,10 +17,6 @@ import { ServiceProviderMetadata } from './generators/ServiceProviderMetadata'; import { LogoutRequestParser } from './parsers/LogoutRequest'; import { LogoutResponseParser } from './parsers/LogoutResponse'; import { ResponseParser } from './parsers/Response'; -import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; -import type { ISAMLRequest } from '../definition/ISAMLRequest'; -import type { ILogoutResponse } from '../definition/ILogoutResponse'; -import type { ILogoutRequestValidateCallback, ILogoutResponseValidateCallback, IResponseValidateCallback } from '../definition/callbacks'; export class SAMLServiceProvider { serviceProviderOptions: IServiceProviderOptions; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/Utils.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/Utils.ts index 608f2d524063..70df22120b75 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/Utils.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/Utils.ts @@ -1,13 +1,14 @@ -import zlib from 'zlib'; import { EventEmitter } from 'events'; +import zlib from 'zlib'; -import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; -import type { ISAMLUser } from '../definition/ISAMLUser'; -import type { ISAMLGlobalSettings } from '../definition/ISAMLGlobalSettings'; +import type { Logger } from '@rocket.chat/logger'; + +import { ensureArray } from '../../../../lib/utils/arrayUtils'; import type { IUserDataMap, IAttributeMapping } from '../definition/IAttributeMapping'; +import type { ISAMLGlobalSettings } from '../definition/ISAMLGlobalSettings'; +import type { ISAMLUser } from '../definition/ISAMLUser'; +import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; import { StatusCode } from './constants'; -import type { Logger } from '../../../../server/lib/logger/Logger'; -import { ensureArray } from '../../../../lib/utils/arrayUtils'; let providerList: Array = []; let debug = false; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts index 6b5f9d912e52..9f264d7e9a05 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts @@ -1,3 +1,6 @@ +import type { IAuthorizeRequestVariables } from '../../definition/IAuthorizeRequestVariables'; +import type { ISAMLRequest } from '../../definition/ISAMLRequest'; +import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import { SAMLUtils } from '../Utils'; import { defaultIdentifierFormat, @@ -6,9 +9,6 @@ import { defaultNameIDTemplate, defaultAuthnContextTemplate, } from '../constants'; -import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; -import type { ISAMLRequest } from '../../definition/ISAMLRequest'; -import type { IAuthorizeRequestVariables } from '../../definition/IAuthorizeRequestVariables'; /* An Authorize Request is used to show the Identity Provider login form when the user clicks on the Rocket.Chat SAML login button diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutRequest.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutRequest.ts index 09ab4aab4dc6..ebca0b4b45f8 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutRequest.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutRequest.ts @@ -1,8 +1,8 @@ +import type { ILogoutRequestVariables } from '../../definition/ILogoutRequestVariables'; +import type { ISAMLRequest } from '../../definition/ISAMLRequest'; +import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import { SAMLUtils } from '../Utils'; import { defaultIdentifierFormat, defaultLogoutRequestTemplate } from '../constants'; -import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; -import type { ISAMLRequest } from '../../definition/ISAMLRequest'; -import type { ILogoutRequestVariables } from '../../definition/ILogoutRequestVariables'; /* A Logout Request is used when the user is logged out of Rocket.Chat and the Service Provider is configured to also logout from the Identity Provider. diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutResponse.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutResponse.ts index 9504376f7bf8..a2563cede90f 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutResponse.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/LogoutResponse.ts @@ -1,8 +1,8 @@ -import { SAMLUtils } from '../Utils'; -import { defaultLogoutResponseTemplate, defaultIdentifierFormat } from '../constants'; -import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import type { ILogoutResponse } from '../../definition/ILogoutResponse'; import type { ILogoutResponseVariables } from '../../definition/ILogoutResponseVariables'; +import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; +import { SAMLUtils } from '../Utils'; +import { defaultLogoutResponseTemplate, defaultIdentifierFormat } from '../constants'; /* A Logout Response is used when the Identity Provider (IdP) sends us a Logout Request. diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/ServiceProviderMetadata.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/ServiceProviderMetadata.ts index 47bca7933405..09d5bb78f4db 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/ServiceProviderMetadata.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/ServiceProviderMetadata.ts @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; -import { defaultIdentifierFormat, defaultMetadataCertificateTemplate, defaultMetadataTemplate } from '../constants'; -import { SAMLUtils } from '../Utils'; -import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import type { IMetadataVariables } from '../../definition/IMetadataVariables'; +import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; +import { SAMLUtils } from '../Utils'; +import { defaultIdentifierFormat, defaultMetadataCertificateTemplate, defaultMetadataTemplate } from '../constants'; /* The metadata will be available at the following url: diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts index f3fcbd22672f..bbae84556a9a 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutRequest.ts @@ -1,8 +1,8 @@ import xmldom from '@xmldom/xmldom'; -import { SAMLUtils } from '../Utils'; import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import type { ILogoutRequestValidateCallback } from '../../definition/callbacks'; +import { SAMLUtils } from '../Utils'; export class LogoutRequestParser { serviceProviderOptions: IServiceProviderOptions; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts index 7ac0e0a1bb38..54db1a675c9a 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/LogoutResponse.ts @@ -1,8 +1,8 @@ import xmldom from '@xmldom/xmldom'; -import { SAMLUtils } from '../Utils'; import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import type { ILogoutResponseValidateCallback } from '../../definition/callbacks'; +import { SAMLUtils } from '../Utils'; export class LogoutResponseParser { serviceProviderOptions: IServiceProviderOptions; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts index 8dd60c68a94c..af052f43b7fe 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/parsers/Response.ts @@ -1,12 +1,12 @@ import xmldom from '@xmldom/xmldom'; -import xmlenc from 'xml-encryption'; import xmlCrypto from 'xml-crypto'; +import xmlenc from 'xml-encryption'; -import { SAMLUtils } from '../Utils'; -import { StatusCode } from '../constants'; +import type { ISAMLAssertion } from '../../definition/ISAMLAssertion'; import type { IServiceProviderOptions } from '../../definition/IServiceProviderOptions'; import type { IResponseValidateCallback } from '../../definition/callbacks'; -import type { ISAMLAssertion } from '../../definition/ISAMLAssertion'; +import { SAMLUtils } from '../Utils'; +import { StatusCode } from '../constants'; type XmlParent = Element | Document; @@ -210,7 +210,7 @@ export class ResponseParser { if (typeof encAssertion !== 'undefined') { const options = { key: this.serviceProviderOptions.privateKey }; const encData = encAssertion.getElementsByTagNameNS('*', 'EncryptedData')[0]; - xmlenc.decrypt(encData, options, function (err, result) { + xmlenc.decrypt(encData, options, (err, result) => { if (err) { SAMLUtils.error(err); } diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts index cd139a338c5a..31bb8e37cfac 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; -import { settings, settingsRegistry } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings, settingsRegistry } from '../../../settings/server'; import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; import { SAMLUtils } from './Utils'; import { diff --git a/apps/meteor/app/meteor-accounts-saml/server/listener.ts b/apps/meteor/app/meteor-accounts-saml/server/listener.ts index 894f84fe1197..8dd449597681 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/listener.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/listener.ts @@ -1,15 +1,15 @@ import type { IncomingMessage, ServerResponse } from 'http'; +import type { IIncomingMessage } from '@rocket.chat/core-typings'; +import bodyParser from 'body-parser'; import { Meteor } from 'meteor/meteor'; -import { WebApp } from 'meteor/webapp'; import { RoutePolicy } from 'meteor/routepolicy'; -import bodyParser from 'body-parser'; -import type { IIncomingMessage } from '@rocket.chat/core-typings'; +import { WebApp } from 'meteor/webapp'; import { SystemLogger } from '../../../server/lib/logger/system'; +import type { ISAMLAction } from './definition/ISAMLAction'; import { SAML } from './lib/SAML'; import { SAMLUtils } from './lib/Utils'; -import type { ISAMLAction } from './definition/ISAMLAction'; RoutePolicy.declare('/_saml/', 'network'); diff --git a/apps/meteor/app/meteor-accounts-saml/server/loginHandler.ts b/apps/meteor/app/meteor-accounts-saml/server/loginHandler.ts index 6220465dd945..4adbfc7e0674 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/loginHandler.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/loginHandler.ts @@ -1,17 +1,17 @@ -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { SAMLUtils } from './lib/Utils'; -import { SAML } from './lib/SAML'; -import { SystemLogger } from '../../../server/lib/logger/system'; import { i18n } from '../../../server/lib/i18n'; +import { SystemLogger } from '../../../server/lib/logger/system'; +import { SAML } from './lib/SAML'; +import { SAMLUtils } from './lib/Utils'; const makeError = (message: string): Record => ({ type: 'saml', error: new Meteor.Error(Accounts.LoginCancelledError.numericError, message), }); -Accounts.registerLoginHandler('saml', async function (loginRequest) { +Accounts.registerLoginHandler('saml', async (loginRequest) => { if (!loginRequest.saml || !loginRequest.credentialToken || typeof loginRequest.credentialToken !== 'string') { return undefined; } diff --git a/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts b/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts index 877e3f96b9b9..2b960059f164 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; import { SAMLServiceProvider } from '../lib/ServiceProvider'; import { SAMLUtils } from '../lib/Utils'; -import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; /** * Fetch SAML provider configs for given 'provider'. diff --git a/apps/meteor/app/meteor-accounts-saml/server/startup.ts b/apps/meteor/app/meteor-accounts-saml/server/startup.ts index 1b7287bcc60b..7a2bf16d3244 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/startup.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/startup.ts @@ -1,9 +1,9 @@ +import { Logger } from '@rocket.chat/logger'; import { Meteor } from 'meteor/meteor'; import { settings } from '../../settings/server'; -import { loadSamlServiceProviders, addSettings } from './lib/settings'; -import { Logger } from '../../logger/server'; import { SAMLUtils } from './lib/Utils'; +import { loadSamlServiceProviders, addSettings } from './lib/settings'; const logger = new Logger('steffo:meteor-accounts-saml'); SAMLUtils.setLoggerInstance(logger); diff --git a/apps/meteor/app/metrics/server/lib/collectMetrics.ts b/apps/meteor/app/metrics/server/lib/collectMetrics.ts index 44e193fa2fe5..8213e27ed4f4 100644 --- a/apps/meteor/app/metrics/server/lib/collectMetrics.ts +++ b/apps/meteor/app/metrics/server/lib/collectMetrics.ts @@ -1,20 +1,20 @@ import http from 'http'; -import client from 'prom-client'; +import { Statistics } from '@rocket.chat/models'; import connect from 'connect'; -import _ from 'underscore'; -import gcStats from 'prometheus-gc-stats'; +import { Facts } from 'meteor/facts-base'; import { Meteor } from 'meteor/meteor'; import { MongoInternals } from 'meteor/mongo'; -import { Facts } from 'meteor/facts-base'; -import { Statistics } from '@rocket.chat/models'; +import client from 'prom-client'; +import gcStats from 'prometheus-gc-stats'; +import _ from 'underscore'; -import { Info } from '../../../utils/server'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { getControl } from '../../../../server/lib/migrations'; import { settings } from '../../../settings/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; -import { metrics } from './metrics'; import { getAppsStatistics } from '../../../statistics/server/lib/getAppsStatistics'; +import { Info } from '../../../utils/rocketchat.info'; +import { metrics } from './metrics'; const { mongo } = MongoInternals.defaultRemoteCollectionDriver(); diff --git a/apps/meteor/app/models/client/index.ts b/apps/meteor/app/models/client/index.ts index d9d1d5c2874a..b4c540318a9b 100644 --- a/apps/meteor/app/models/client/index.ts +++ b/apps/meteor/app/models/client/index.ts @@ -1,20 +1,20 @@ import { Base } from './models/Base'; -import { Roles } from './models/Roles'; -import { Users } from './models/Users'; import { CachedChannelList } from './models/CachedChannelList'; import { CachedChatRoom } from './models/CachedChatRoom'; import { CachedChatSubscription } from './models/CachedChatSubscription'; import { CachedUserList } from './models/CachedUserList'; +import { ChatMessage } from './models/ChatMessage'; +import { AuthzCachedCollection, ChatPermissions } from './models/ChatPermissions'; import { ChatRoom } from './models/ChatRoom'; import { ChatSubscription } from './models/ChatSubscription'; -import { ChatMessage } from './models/ChatMessage'; +import CustomSounds from './models/CustomSounds'; +import EmojiCustom from './models/EmojiCustom'; +import { Roles } from './models/Roles'; import { RoomRoles } from './models/RoomRoles'; import { UserAndRoom } from './models/UserAndRoom'; import { UserRoles } from './models/UserRoles'; -import { AuthzCachedCollection, ChatPermissions } from './models/ChatPermissions'; +import { Users } from './models/Users'; import { WebdavAccounts } from './models/WebdavAccounts'; -import CustomSounds from './models/CustomSounds'; -import EmojiCustom from './models/EmojiCustom'; // overwrite Meteor.users collection so records on it don't get erased whenever the client reconnects to websocket const meteorUserOverwrite = () => { diff --git a/apps/meteor/app/models/client/models/CachedChatRoom.ts b/apps/meteor/app/models/client/models/CachedChatRoom.ts index 8749cc4de1a0..f66e5b447432 100644 --- a/apps/meteor/app/models/client/models/CachedChatRoom.ts +++ b/apps/meteor/app/models/client/models/CachedChatRoom.ts @@ -1,8 +1,8 @@ import type { IOmnichannelRoom, IRoom, IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; import { DEFAULT_SLA_CONFIG, LivechatPriorityWeight } from '@rocket.chat/core-typings'; -import { ChatSubscription } from './ChatSubscription'; -import { CachedCollection } from '../../../ui-cached-collection/client'; +import { CachedCollection } from '../../../ui-cached-collection/client/models/CachedCollection'; +import { CachedChatSubscription } from './CachedChatSubscription'; class CachedChatRoom extends CachedCollection { constructor() { @@ -22,12 +22,12 @@ class CachedChatRoom extends CachedCollection { } private mergeWithSubscription(room: IRoom): IRoom { - const sub = ChatSubscription.findOne({ rid: room._id }); + const sub = CachedChatSubscription.collection.findOne({ rid: room._id }); if (!sub) { return room; } - ChatSubscription.update( + CachedChatSubscription.collection.update( { rid: room._id, }, @@ -82,7 +82,7 @@ class CachedChatRoom extends CachedCollection { }, ); - ChatSubscription.update( + CachedChatSubscription.collection.update( { rid: room._id, lm: { $lt: room.lm }, diff --git a/apps/meteor/app/models/client/models/CachedChatSubscription.ts b/apps/meteor/app/models/client/models/CachedChatSubscription.ts index abf60d35b9b1..0e325453539a 100644 --- a/apps/meteor/app/models/client/models/CachedChatSubscription.ts +++ b/apps/meteor/app/models/client/models/CachedChatSubscription.ts @@ -2,8 +2,8 @@ import type { IOmnichannelRoom, IRoomWithRetentionPolicy, ISubscription } from ' import { DEFAULT_SLA_CONFIG, LivechatPriorityWeight } from '@rocket.chat/core-typings'; import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; -import { CachedCollection } from '../../../ui-cached-collection/client'; -import { ChatRoom } from './ChatRoom'; +import { CachedCollection } from '../../../ui-cached-collection/client/models/CachedCollection'; +import { CachedChatRoom } from './CachedChatRoom'; declare module '@rocket.chat/core-typings' { interface ISubscription { @@ -72,7 +72,7 @@ class CachedChatSubscription extends CachedCollection implements MinimongoCollection { ready = new ReactiveVar(false); diff --git a/apps/meteor/app/models/client/models/RoomRoles.ts b/apps/meteor/app/models/client/models/RoomRoles.ts index 21cb440b656d..bb9fabbedbf2 100644 --- a/apps/meteor/app/models/client/models/RoomRoles.ts +++ b/apps/meteor/app/models/client/models/RoomRoles.ts @@ -1,5 +1,5 @@ -import { Mongo } from 'meteor/mongo'; import type { ISubscription } from '@rocket.chat/core-typings'; +import { Mongo } from 'meteor/mongo'; /** @deprecated */ export const RoomRoles = new Mongo.Collection>(null); diff --git a/apps/meteor/app/models/client/models/UserRoles.ts b/apps/meteor/app/models/client/models/UserRoles.ts index c3592fe51d12..9e68239bdb44 100644 --- a/apps/meteor/app/models/client/models/UserRoles.ts +++ b/apps/meteor/app/models/client/models/UserRoles.ts @@ -1,5 +1,5 @@ -import { Mongo } from 'meteor/mongo'; import type { IRocketChatRecord, IRole } from '@rocket.chat/core-typings'; +import { Mongo } from 'meteor/mongo'; /** @deprecated */ export const UserRoles = new Mongo.Collection< diff --git a/apps/meteor/app/models/client/models/Users.ts b/apps/meteor/app/models/client/models/Users.ts index fef23ae1f223..089486f3a20c 100644 --- a/apps/meteor/app/models/client/models/Users.ts +++ b/apps/meteor/app/models/client/models/Users.ts @@ -1,5 +1,5 @@ -import { Mongo } from 'meteor/mongo'; import type { IRole, IUser } from '@rocket.chat/core-typings'; +import { Mongo } from 'meteor/mongo'; class UsersCollection extends Mongo.Collection { constructor() { diff --git a/apps/meteor/app/nextcloud/client/lib.ts b/apps/meteor/app/nextcloud/client/lib.ts index f3562a3c2585..12a54217691c 100644 --- a/apps/meteor/app/nextcloud/client/lib.ts +++ b/apps/meteor/app/nextcloud/client/lib.ts @@ -1,10 +1,10 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/client'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; +import { settings } from '../../settings/client'; const config: OauthConfig = { serverURL: '', @@ -33,8 +33,8 @@ const fillServerURL = _.debounce((): void => { return Nextcloud.configure(config); }, 100); -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { return fillServerURL(); }); }); diff --git a/apps/meteor/app/nextcloud/server/addWebdavServer.ts b/apps/meteor/app/nextcloud/server/addWebdavServer.ts index eed0f76c6b38..f53ff59d76ef 100644 --- a/apps/meteor/app/nextcloud/server/addWebdavServer.ts +++ b/apps/meteor/app/nextcloud/server/addWebdavServer.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings/server'; import { SystemLogger } from '../../../server/lib/logger/system'; +import { settings } from '../../settings/server'; import { addWebdavAccountByToken } from '../../webdav/server/methods/addWebdavAccount'; Meteor.startup(() => { diff --git a/apps/meteor/app/nextcloud/server/lib.ts b/apps/meteor/app/nextcloud/server/lib.ts index 5f2830b5dd42..28cf52da57a1 100644 --- a/apps/meteor/app/nextcloud/server/lib.ts +++ b/apps/meteor/app/nextcloud/server/lib.ts @@ -1,9 +1,9 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/server'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { settings } from '../../settings/server'; const config: OauthConfig = { serverURL: '', @@ -32,6 +32,6 @@ const fillServerURL = _.debounce((): void => { return Nextcloud.configure(config); }, 1000); -Meteor.startup(function () { +Meteor.startup(() => { settings.watch('Accounts_OAuth_Nextcloud_URL', () => fillServerURL()); }); diff --git a/apps/meteor/app/notification-queue/server/NotificationQueue.ts b/apps/meteor/app/notification-queue/server/NotificationQueue.ts index 29b0fe60e2b4..f58eaec3f576 100644 --- a/apps/meteor/app/notification-queue/server/NotificationQueue.ts +++ b/apps/meteor/app/notification-queue/server/NotificationQueue.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import type { INotification, INotificationItemPush, INotificationItemEmail, NotificationItem, IUser } from '@rocket.chat/core-typings'; import { NotificationQueue, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; +import { SystemLogger } from '../../../server/lib/logger/system'; import { sendEmailFromData } from '../../lib/server/functions/notifications/email'; import { PushNotification } from '../../push-notifications/server'; -import { SystemLogger } from '../../../server/lib/logger/system'; const { NOTIFICATIONS_WORKER_TIMEOUT = 2000, diff --git a/apps/meteor/app/notifications/client/lib/Notifications.ts b/apps/meteor/app/notifications/client/lib/Notifications.ts index 1b548c4ada10..8d77b9e3a0cb 100644 --- a/apps/meteor/app/notifications/client/lib/Notifications.ts +++ b/apps/meteor/app/notifications/client/lib/Notifications.ts @@ -1,6 +1,6 @@ +import type { StreamKeys, StreamerCallback } from '@rocket.chat/ddp-client/src/types/streams'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import type { StreamKeys, StreamerCallback } from '@rocket.chat/ddp-client/src/types/streams'; import './Presence'; import { sdk } from '../../../utils/client/lib/SDKClient'; diff --git a/apps/meteor/app/notifications/client/lib/Presence.ts b/apps/meteor/app/notifications/client/lib/Presence.ts index 0c04a164d936..69c255aaf1f9 100644 --- a/apps/meteor/app/notifications/client/lib/Presence.ts +++ b/apps/meteor/app/notifications/client/lib/Presence.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import type { StreamerEvents } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { Presence, STATUS_MAP } from '../../../../client/lib/presence'; diff --git a/apps/meteor/app/notifications/server/lib/Notifications.ts b/apps/meteor/app/notifications/server/lib/Notifications.ts index 7a58894addab..ed4985777414 100644 --- a/apps/meteor/app/notifications/server/lib/Notifications.ts +++ b/apps/meteor/app/notifications/server/lib/Notifications.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { DDPCommon } from 'meteor/ddp-common'; import { api } from '@rocket.chat/core-services'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { DDPCommon } from 'meteor/ddp-common'; +import { Meteor } from 'meteor/meteor'; import { NotificationsModule } from '../../../../server/modules/notifications/notifications.module'; import { Streamer } from '../../../../server/modules/streamer/streamer.module'; diff --git a/apps/meteor/app/notifications/server/lib/Presence.ts b/apps/meteor/app/notifications/server/lib/Presence.ts index 5f258c05f998..9955b687701f 100644 --- a/apps/meteor/app/notifications/server/lib/Presence.ts +++ b/apps/meteor/app/notifications/server/lib/Presence.ts @@ -1,7 +1,7 @@ -import { Emitter } from '@rocket.chat/emitter'; -import type { IPublication, IStreamerConstructor, Connection, IStreamer } from 'meteor/rocketchat:streamer'; import type { IUser } from '@rocket.chat/core-typings'; +import { Emitter } from '@rocket.chat/emitter'; import type { StreamerEvents } from '@rocket.chat/ui-contexts'; +import type { IPublication, IStreamerConstructor, Connection, IStreamer } from 'meteor/rocketchat:streamer'; type UserPresenceStreamProps = { added: IUser['_id'][]; diff --git a/apps/meteor/app/oauth2-server-config/server/admin/functions/addOAuthApp.ts b/apps/meteor/app/oauth2-server-config/server/admin/functions/addOAuthApp.ts index 0584d0819654..8e828f5c11d9 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/functions/addOAuthApp.ts +++ b/apps/meteor/app/oauth2-server-config/server/admin/functions/addOAuthApp.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; +import type { IOAuthApps, IUser } from '@rocket.chat/core-typings'; import { OAuthApps, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; import type { OauthAppsAddParams } from '@rocket.chat/rest-typings'; -import type { IOAuthApps, IUser } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { parseUriList } from './parseUriList'; diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts b/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts index 1b099fe119b6..0209f9d453b5 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { OAuthAccessTokens, OAuthApps, OAuthAuthCodes } from '@rocket.chat/models'; import type { IOAuthApps } from '@rocket.chat/core-typings'; +import { OAuthAccessTokens, OAuthApps, OAuthAuthCodes } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts b/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts index d175fec64816..525d0fa2d124 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { OAuthApps, Users } from '@rocket.chat/models'; import type { IOAuthApps } from '@rocket.chat/core-typings'; +import { OAuthApps, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { parseUriList } from '../functions/parseUriList'; diff --git a/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts b/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts index 45a1a4bc87b8..bcc362ba82e9 100644 --- a/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts +++ b/apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { WebApp } from 'meteor/webapp'; -import type { Request, Response } from 'express'; import type { IUser } from '@rocket.chat/core-typings'; import { OAuthAccessTokens, Users } from '@rocket.chat/models'; +import type { Request, Response } from 'express'; +import { Meteor } from 'meteor/meteor'; +import { WebApp } from 'meteor/webapp'; -import { API } from '../../../api/server'; import { OAuth2Server } from '../../../../server/oauth2-server/oauth'; +import { API } from '../../../api/server'; const oauth2server = new OAuth2Server({ // If you're developing something related to oauth servers, you should change this to true @@ -44,7 +44,7 @@ oauth2server.app.disable('x-powered-by'); WebApp.connectHandlers.use(oauth2server.app); -oauth2server.app.get('/oauth/userinfo', async function (req: Request, res: Response) { +oauth2server.app.get('/oauth/userinfo', async (req: Request, res: Response) => { if (req.headers.authorization == null) { return res.status(401).send('No token'); } diff --git a/apps/meteor/app/oembed/server/jumpToMessage.ts b/apps/meteor/app/oembed/server/jumpToMessage.ts index 78a5c6277d16..9c5be2639c7c 100644 --- a/apps/meteor/app/oembed/server/jumpToMessage.ts +++ b/apps/meteor/app/oembed/server/jumpToMessage.ts @@ -1,15 +1,14 @@ -import URL from 'url'; import QueryString from 'querystring'; +import URL from 'url'; -import { Meteor } from 'meteor/meteor'; import type { MessageAttachment, IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { isQuoteAttachment } from '@rocket.chat/core-typings'; import { Messages, Users, Rooms } from '@rocket.chat/models'; -import { createQuoteAttachment } from '../../../lib/createQuoteAttachment'; -import { settings } from '../../settings/server'; import { callbacks } from '../../../lib/callbacks'; +import { createQuoteAttachment } from '../../../lib/createQuoteAttachment'; import { canAccessRoomAsync } from '../../authorization/server/functions/canAccessRoom'; +import { settings } from '../../settings/server'; import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; const recursiveRemoveAttachments = (attachments: MessageAttachment, deep = 1, quoteChainLimit: number): MessageAttachment => { @@ -51,7 +50,7 @@ callbacks.add( for await (const item of msg.urls) { // if the URL doesn't belong to the current server, skip - if (!item.url.includes(Meteor.absoluteUrl())) { + if (!item.url.includes(settings.get('Site_Url'))) { continue; } diff --git a/apps/meteor/app/oembed/server/providers.ts b/apps/meteor/app/oembed/server/providers.ts index aaa46ab2a155..e80e456c679b 100644 --- a/apps/meteor/app/oembed/server/providers.ts +++ b/apps/meteor/app/oembed/server/providers.ts @@ -1,9 +1,9 @@ -import URL from 'url'; import QueryString from 'querystring'; +import URL from 'url'; +import type { OEmbedMeta, OEmbedUrlContent, ParsedUrl, OEmbedProvider } from '@rocket.chat/core-typings'; import { camelCase } from 'change-case'; import _ from 'underscore'; -import type { OEmbedMeta, OEmbedUrlContent, ParsedUrl, OEmbedProvider } from '@rocket.chat/core-typings'; import { callbacks } from '../../../lib/callbacks'; import { SystemLogger } from '../../../server/lib/logger/system'; @@ -31,9 +31,9 @@ class Providers { } getProviderForUrl(url: string): OEmbedProvider | undefined { - return this.providers?.find(function (provider) { + return this.providers?.find((provider) => { return ( - provider.urls?.some(function (re) { + provider.urls?.some((re) => { return re.test(url); }) ?? false ); @@ -94,7 +94,7 @@ providers.registerProvider({ callbacks.add( 'oembed:beforeGetUrlContent', - function (data) { + (data) => { if (data.parsedUrl != null) { const url = URL.format(data.parsedUrl); const provider = providers.getProviderForUrl(url); @@ -147,7 +147,7 @@ const cleanupOembed = (data: { callbacks.add( 'oembed:afterParseContent', - function (data) { + (data) => { if (!data?.url || !data.content?.body || !data.parsedUrl?.query) { return cleanupOembed(data); } diff --git a/apps/meteor/app/oembed/server/server.ts b/apps/meteor/app/oembed/server/server.ts index 51702700ba10..256722cdd3d4 100644 --- a/apps/meteor/app/oembed/server/server.ts +++ b/apps/meteor/app/oembed/server/server.ts @@ -1,23 +1,24 @@ -import URL from 'url'; import querystring from 'querystring'; +import URL from 'url'; -import { camelCase } from 'change-case'; -import _ from 'underscore'; -import iconv from 'iconv-lite'; -import ipRangeCheck from 'ip-range-check'; -import he from 'he'; -import jschardet from 'jschardet'; import type { OEmbedUrlContentResult, OEmbedUrlWithMetadata, IMessage, MessageAttachment, OEmbedMeta } from '@rocket.chat/core-typings'; import { isOEmbedUrlContentResult, isOEmbedUrlWithMetadata } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import { Messages, OEmbedCache } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { camelCase } from 'change-case'; +import he from 'he'; +import iconv from 'iconv-lite'; +import ipRangeCheck from 'ip-range-check'; +import jschardet from 'jschardet'; +import _ from 'underscore'; -import { Logger } from '../../logger/server'; import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings/server'; import { isURL } from '../../../lib/utils/isURL'; -import { Info } from '../../utils/server'; +import { settings } from '../../settings/server'; +import { Info } from '../../utils/rocketchat.info'; +const MAX_EXTERNAL_URL_PREVIEWS = 5; const log = new Logger('OEmbed'); // Detect encoding // Priority: @@ -185,19 +186,19 @@ const getUrlMeta = async function ( metas[name] = metas[name] || he.unescape(value); return metas[name]; }; - content.body.replace(/]*>([^<]*)<\/title>/gim, function (_meta, title) { + content.body.replace(/]*>([^<]*)<\/title>/gim, (_meta, title) => { return escapeMeta('pageTitle', title); }); - content.body.replace(/]*(?:name|property)=[']([^']*)['][^>]*\scontent=[']([^']*)['][^>]*>/gim, function (_meta, name, value) { + content.body.replace(/]*(?:name|property)=[']([^']*)['][^>]*\scontent=[']([^']*)['][^>]*>/gim, (_meta, name, value) => { return escapeMeta(camelCase(name), value); }); - content.body.replace(/]*(?:name|property)=["]([^"]*)["][^>]*\scontent=["]([^"]*)["][^>]*>/gim, function (_meta, name, value) { + content.body.replace(/]*(?:name|property)=["]([^"]*)["][^>]*\scontent=["]([^"]*)["][^>]*>/gim, (_meta, name, value) => { return escapeMeta(camelCase(name), value); }); - content.body.replace(/]*\scontent=[']([^']*)['][^>]*(?:name|property)=[']([^']*)['][^>]*>/gim, function (_meta, value, name) { + content.body.replace(/]*\scontent=[']([^']*)['][^>]*(?:name|property)=[']([^']*)['][^>]*>/gim, (_meta, value, name) => { return escapeMeta(camelCase(name), value); }); - content.body.replace(/]*\scontent=["]([^"]*)["][^>]*(?:name|property)=["]([^"]*)["][^>]*>/gim, function (_meta, value, name) { + content.body.replace(/]*\scontent=["]([^"]*)["][^>]*(?:name|property)=["]([^"]*)["][^>]*>/gim, (_meta, value, name) => { return escapeMeta(camelCase(name), value); }); if (metas.fragment === '!' && withFragment == null) { @@ -287,16 +288,25 @@ const rocketUrlParser = async function (message: IMessage): Promise { log.debug('Parsing message URLs'); if (Array.isArray(message.urls)) { log.debug('URLs found', message.urls.length); + + if ( + (message.attachments && message.attachments.length > 0) || + message.urls.filter((item) => !item.url.includes(settings.get('Site_Url'))).length > MAX_EXTERNAL_URL_PREVIEWS + ) { + log.debug('All URL ignored'); + return message; + } + const attachments: MessageAttachment[] = []; let changed = false; for await (const item of message.urls) { if (item.ignoreParse === true) { log.debug('URL ignored', item.url); - break; + continue; } if (!isURL(item.url)) { - break; + continue; } const data = await getUrlMetaWithCache(item.url); if (data != null) { @@ -320,7 +330,7 @@ const rocketUrlParser = async function (message: IMessage): Promise { changed = true; } } - if (attachments.length) { + if (attachments.length > 0) { await Messages.setMessageAttachments(message._id, attachments); } if (changed === true) { @@ -340,7 +350,7 @@ const OEmbed: { getUrlMeta, }; -settings.watch('API_Embed', function (value) { +settings.watch('API_Embed', (value) => { if (value) { return callbacks.add('afterSaveMessage', (message) => OEmbed.rocketUrlParser(message), callbacks.priority.LOW, 'API_Embed'); } diff --git a/apps/meteor/app/otr/client/OTR.ts b/apps/meteor/app/otr/client/OTR.ts index 0573bc5739ac..55e0a1289a67 100644 --- a/apps/meteor/app/otr/client/OTR.ts +++ b/apps/meteor/app/otr/client/OTR.ts @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; -import type { IOTR } from '../lib/IOTR'; import { Subscriptions } from '../../models/client'; +import type { IOTR } from '../lib/IOTR'; import { OTRRoom } from './OTRRoom'; class OTR implements IOTR { diff --git a/apps/meteor/app/otr/client/OTRRoom.ts b/apps/meteor/app/otr/client/OTRRoom.ts index 0ac549d526f9..d0d1ae84cd3e 100644 --- a/apps/meteor/app/otr/client/OTRRoom.ts +++ b/apps/meteor/app/otr/client/OTRRoom.ts @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import { Random } from '@rocket.chat/random'; import EJSON from 'ejson'; import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; @@ -12,8 +12,11 @@ import { dispatchToastMessage } from '../../../client/lib/toast'; import { getUidDirectMessage } from '../../../client/lib/utils/getUidDirectMessage'; import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; import { Notifications } from '../../notifications/client'; -import { otrSystemMessages } from '../lib/constants'; +import { sdk } from '../../utils/client/lib/SDKClient'; +import { t } from '../../utils/lib/i18n'; import type { IOnUserStreamData, IOTRAlgorithm, IOTRDecrypt, IOTRRoom } from '../lib/IOTR'; +import { OtrRoomState } from '../lib/OtrRoomState'; +import { otrSystemMessages } from '../lib/constants'; import { decryptAES, deriveBits, @@ -25,9 +28,6 @@ import { importKeyRaw, joinEncryptedData, } from '../lib/functions'; -import { OtrRoomState } from '../lib/OtrRoomState'; -import { t } from '../../utils/lib/i18n'; -import { sdk } from '../../utils/client/lib/SDKClient'; export class OTRRoom implements IOTRRoom { private _userId: string; diff --git a/apps/meteor/app/otr/client/index.ts b/apps/meteor/app/otr/client/index.ts index c0546404ab88..74fea3c003e8 100644 --- a/apps/meteor/app/otr/client/index.ts +++ b/apps/meteor/app/otr/client/index.ts @@ -1,4 +1,3 @@ import './OTRRoom'; import './OTR'; -import './tabBar'; import './messageTypes'; diff --git a/apps/meteor/app/otr/client/tabBar.ts b/apps/meteor/app/otr/client/tabBar.ts deleted file mode 100644 index dd56bd368a8b..000000000000 --- a/apps/meteor/app/otr/client/tabBar.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useMemo, lazy, useEffect } from 'react'; -import { useSetting } from '@rocket.chat/ui-contexts'; -import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; -import { isRoomFederated } from '@rocket.chat/core-typings'; - -import OTR from './OTR'; -import { addAction } from '../../../client/views/room/lib/Toolbox'; - -const template = lazy(() => import('../../../client/views/room/contextualBar/OTR')); - -addAction('otr', (options) => { - const room = options.room as unknown as ISubscription & IRoom; - const federated = isRoomFederated(room); - const enabled = useSetting('OTR_Enable') as boolean; - - const shouldAddAction = enabled && Boolean(global.crypto); - - useEffect(() => { - OTR.setEnabled(shouldAddAction); - }, [shouldAddAction]); - - return useMemo( - () => - shouldAddAction - ? { - groups: ['direct'], - id: 'otr', - title: 'OTR', - icon: 'stopwatch', - template, - order: 13, - full: true, - ...(federated && { - 'data-tooltip': 'OTR_unavailable_for_federation', - 'disabled': true, - }), - } - : null, - [shouldAddAction, federated], - ); -}); diff --git a/apps/meteor/app/otr/lib/IOTR.ts b/apps/meteor/app/otr/lib/IOTR.ts index b9d3bc3b473c..051752ab947f 100644 --- a/apps/meteor/app/otr/lib/IOTR.ts +++ b/apps/meteor/app/otr/lib/IOTR.ts @@ -1,7 +1,7 @@ import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; -import type { OtrRoomState } from './OtrRoomState'; import type { OTRRoom } from '../client/OTRRoom'; +import type { OtrRoomState } from './OtrRoomState'; export interface IOnUserStreamData { roomId: IRoom['_id']; diff --git a/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts b/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts index 916dc5759303..a994b19dd1a4 100644 --- a/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts +++ b/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import { Messages, Subscriptions, ReadReceipts } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Messages, Subscriptions } from '@rocket.chat/models'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -28,5 +28,6 @@ Meteor.methods({ } await Messages.deleteOldOTRMessages(roomId, now); + await ReadReceipts.removeOTRReceiptsUntilDate(roomId, now); }, }); diff --git a/apps/meteor/app/push-notifications/client/index.ts b/apps/meteor/app/push-notifications/client/index.ts deleted file mode 100644 index cd95da0a7d92..000000000000 --- a/apps/meteor/app/push-notifications/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './tabBar'; diff --git a/apps/meteor/app/push-notifications/client/tabBar.ts b/apps/meteor/app/push-notifications/client/tabBar.ts deleted file mode 100644 index 1312018a1cde..000000000000 --- a/apps/meteor/app/push-notifications/client/tabBar.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { lazy, useMemo } from 'react'; -import { useUserSubscription } from '@rocket.chat/ui-contexts'; - -import { addAction } from '../../../client/views/room/lib/Toolbox'; - -addAction('push-notifications', ({ room }) => { - const subscription = useUserSubscription(room?._id); - - return useMemo( - () => - subscription - ? { - groups: ['channel', 'group', 'direct', 'direct_multiple', 'team'], - id: 'push-notifications', - title: 'Notifications_Preferences', - icon: 'bell', - template: lazy(() => import('../../../client/views/room/contextualBar/NotificationPreferences')), - order: 8, - } - : null, - [subscription], - ); -}); diff --git a/apps/meteor/app/push-notifications/server/lib/PushNotification.ts b/apps/meteor/app/push-notifications/server/lib/PushNotification.ts index 1925b59dc66a..3e021cfbcc75 100644 --- a/apps/meteor/app/push-notifications/server/lib/PushNotification.ts +++ b/apps/meteor/app/push-notifications/server/lib/PushNotification.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; import type { IMessage, IPushNotificationConfig, IRoom, IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { Push } from '../../../push/server'; -import { settings } from '../../../settings/server'; -import { metrics } from '../../../metrics/server'; +import { callbacks } from '../../../../lib/callbacks'; import { RocketChatAssets } from '../../../assets/server'; import { replaceMentionedUsernamesWithFullNames, parseMessageTextPerUser } from '../../../lib/server/functions/notifications'; -import { callbacks } from '../../../../lib/callbacks'; import { getPushData } from '../../../lib/server/functions/notifications/mobile'; +import { metrics } from '../../../metrics/server'; +import { Push } from '../../../push/server'; +import { settings } from '../../../settings/server'; type PushNotificationData = { rid: string; @@ -65,7 +65,7 @@ class PushNotification { // message is being redacted already by 'getPushData' if idOnly is true const text = !idOnly && roomName !== '' ? `${username}: ${message}` : message; - const config: IPushNotificationConfig = { + return { from: 'push', badge, sound: 'default', @@ -84,15 +84,8 @@ class PushNotification { style: 'inbox', image: RocketChatAssets.getURL('Assets_favicon_192'), }, + ...(category !== '' ? { apn: { category } } : {}), }; - - if (category !== '') { - config.apn = { - category, - }; - } - - return config; } async send({ rid, uid, mid, roomName, username, message, payload, badge = 1, category }: PushNotificationData): Promise { diff --git a/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts b/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts index 521e5c10d7d3..a4376c709275 100644 --- a/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts +++ b/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ISubscription } from '@rocket.chat/core-typings'; import { Subscriptions } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { getUserNotificationPreference } from '../../../utils/server'; +import { getUserNotificationPreference } from '../../../utils/server/getUserNotificationPreference'; const saveAudioNotificationValue = (subId: ISubscription['_id'], value: string) => value === 'default' ? Subscriptions.clearAudioNotificationValueById(subId) : Subscriptions.updateAudioNotificationValueById(subId, value); diff --git a/apps/meteor/app/push/server/apn.js b/apps/meteor/app/push/server/apn.ts similarity index 67% rename from apps/meteor/app/push/server/apn.js rename to apps/meteor/app/push/server/apn.ts index 663c7512ab1a..1794a2886324 100644 --- a/apps/meteor/app/push/server/apn.js +++ b/apps/meteor/app/push/server/apn.ts @@ -1,13 +1,33 @@ +import type { IAppsTokens, RequiredField } from '@rocket.chat/core-typings'; import apn from 'apn'; import EJSON from 'ejson'; +import type { PushOptions, PendingPushNotification } from './definition'; import { logger } from './logger'; -let apnConnection; +let apnConnection: apn.Provider | undefined; -export const sendAPN = ({ userToken, notification, _removeToken }) => { - if (typeof notification.apn === 'object') { - notification = Object.assign({}, notification, notification.apn); +declare module 'apn' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Notification { + setContentAvailable: (value: boolean | 1 | 0) => void; + set category(_value: string | undefined); + set body(_value: string); + set title(_value: string); + } +} + +export const sendAPN = ({ + userToken, + notification, + _removeToken, +}: { + userToken: string; + notification: PendingPushNotification & { topic: string }; + _removeToken: (token: IAppsTokens['token']) => void; +}) => { + if (!apnConnection) { + throw new Error('Apn Connection not initialized.'); } const priority = notification.priority || notification.priority === 0 ? notification.priority : 10; @@ -15,8 +35,13 @@ export const sendAPN = ({ userToken, notification, _removeToken }) => { const note = new apn.Notification(); note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now. - note.badge = notification.badge; - note.sound = notification.sound; + if (notification.badge !== undefined) { + note.badge = notification.badge; + } + + if (notification.sound !== undefined) { + note.sound = notification.sound; + } if (notification.contentAvailable != null) { note.setContentAvailable(notification.contentAvailable); @@ -25,7 +50,7 @@ export const sendAPN = ({ userToken, notification, _removeToken }) => { // adds category support for iOS8 custom actions as described here: // https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/ // RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html#//apple_ref/doc/uid/TP40008194-CH103-SW36 - note.category = notification.category; + note.category = notification.apn?.category; note.body = notification.text; note.title = notification.title; @@ -40,16 +65,14 @@ export const sendAPN = ({ userToken, notification, _removeToken }) => { note.payload.messageFrom = notification.from; note.priority = priority; - // Store the token on the note so we can reference it if there was an error - note.token = userToken; - note.topic = notification.topic; - note.mutableContent = 1; + note.topic = `${notification.topic}${notification.apn?.topicSuffix || ''}`; + note.mutableContent = true; - apnConnection.send(note, userToken).then((response) => { + void apnConnection.send(note, userToken).then((response) => { response.failed.forEach((failure) => { logger.debug(`Got error code ${failure.status} for token ${userToken}`); - if (['400', '410'].includes(failure.status)) { + if (['400', '410'].includes(failure.status ?? '')) { logger.debug(`Removing token ${userToken}`); _removeToken({ apn: userToken, @@ -59,19 +82,10 @@ export const sendAPN = ({ userToken, notification, _removeToken }) => { }); }; -export const initAPN = ({ options, absoluteUrl }) => { +export const initAPN = ({ options, absoluteUrl }: { options: RequiredField; absoluteUrl: string }) => { logger.debug('APN configured'); - // Allow production to be a general option for push notifications - if (options.production === Boolean(options.production)) { - options.apn.production = options.production; - } - - // Give the user warnings about development settings - if (options.apn.development) { - // This flag is normally set by the configuration file - logger.warn('WARNING: Push APN is using development key and certificate'); - } else if (options.apn.gateway) { + if (options.apn.gateway) { // We check the apn gateway i the options, we could risk shipping // server into production while using the production configuration. // On the other hand we could be in development but using the production @@ -93,7 +107,7 @@ export const initAPN = ({ options, absoluteUrl }) => { // Warn about gateways we dont know about logger.warn(`WARNING: Push APN unknown gateway "${options.apn.gateway}"`); } - } else if (options.apn.production) { + } else if (options.production) { if (/http:\/\/localhost/.test(absoluteUrl)) { logger.warn('WARNING: Push APN is configured to production mode - but server is running from localhost'); } @@ -102,12 +116,12 @@ export const initAPN = ({ options, absoluteUrl }) => { } // Check certificate data - if (!options.apn.cert || !options.apn.cert.length) { + if (!options.apn.cert?.length) { logger.error('ERROR: Push server could not find cert'); } // Check key data - if (!options.apn.key || !options.apn.key.length) { + if (!options.apn.key?.length) { logger.error('ERROR: Push server could not find key'); } diff --git a/apps/meteor/app/push/server/definition.ts b/apps/meteor/app/push/server/definition.ts new file mode 100644 index 000000000000..39ab94de0a36 --- /dev/null +++ b/apps/meteor/app/push/server/definition.ts @@ -0,0 +1,46 @@ +export type PushOptions = { + sendTimeout?: number; + production?: boolean; + apn?: { + passphrase: string; + key: string; + cert: string; + + gateway?: string; + }; + gcm?: { + apiKey: string; + projectNumber: string; + }; + gateways?: string[]; + uniqueId: string; + getAuthorization?: () => Promise; +}; + +export type PendingPushNotification = { + from: string; + title: string; + text: string; + badge?: number; + sound?: string; + notId?: number; + apn?: { + category?: string; + topicSuffix?: string; + }; + gcm?: { + style?: string; + image?: string; + }; + payload?: Record; + createdAt: Date; + createdBy?: string; + + userId: string; + + sent?: boolean; + sending?: number; + priority?: number; + + contentAvailable?: 1 | 0; +}; diff --git a/apps/meteor/app/push/server/gcm.js b/apps/meteor/app/push/server/gcm.ts similarity index 65% rename from apps/meteor/app/push/server/gcm.js rename to apps/meteor/app/push/server/gcm.ts index bc90813fe733..6ab679d504f6 100644 --- a/apps/meteor/app/push/server/gcm.js +++ b/apps/meteor/app/push/server/gcm.ts @@ -1,13 +1,23 @@ -import gcm from 'node-gcm'; +import type { IAppsTokens, RequiredField } from '@rocket.chat/core-typings'; import EJSON from 'ejson'; +import gcm from 'node-gcm'; +import type { PendingPushNotification, PushOptions } from './definition'; import { logger } from './logger'; -export const sendGCM = function ({ userTokens, notification, _replaceToken, _removeToken, options }) { - if (typeof notification.gcm === 'object') { - notification = Object.assign({}, notification, notification.gcm); - } - +export const sendGCM = function ({ + userTokens, + notification, + _replaceToken, + _removeToken, + options, +}: { + userTokens: string | string[]; + notification: PendingPushNotification; + _replaceToken: (currentToken: IAppsTokens['token'], newToken: IAppsTokens['token']) => void; + _removeToken: (token: IAppsTokens['token']) => void; + options: RequiredField; +}) { // Make sure userTokens are an array of strings if (typeof userTokens === 'string') { userTokens = [userTokens]; @@ -22,22 +32,14 @@ export const sendGCM = function ({ userTokens, notification, _replaceToken, _rem logger.debug('sendGCM', userTokens, notification); // Allow user to set payload - const data = notification.payload ? { ejson: EJSON.stringify(notification.payload) } : {}; + const data: Record = notification.payload ? { ejson: EJSON.stringify(notification.payload) } : {}; data.title = notification.title; data.message = notification.text; // Set image - if (notification.image != null) { - data.image = notification.image; - } - - if (notification.android_channel_id != null) { - data.android_channel_id = notification.android_channel_id; - } else { - logger.debug( - 'For devices running Android 8.0 or later you are required to provide an android_channel_id. See https://github.com/raix/push/issues/341 for more info', - ); + if (notification.gcm?.image != null) { + data.image = notification.gcm?.image; } // Set extra details @@ -50,24 +52,8 @@ export const sendGCM = function ({ userTokens, notification, _replaceToken, _rem if (notification.notId != null) { data.notId = notification.notId; } - if (notification.style != null) { - data.style = notification.style; - } - if (notification.summaryText != null) { - data.summaryText = notification.summaryText; - } - if (notification.picture != null) { - data.picture = notification.picture; - } - - // Action Buttons - if (notification.actions != null) { - data.actions = notification.actions; - } - - // Force Start - if (notification.forceStart != null) { - data['force-start'] = notification.forceStart; + if (notification.gcm?.style != null) { + data.style = notification.gcm?.style; } if (notification.contentAvailable != null) { @@ -92,7 +78,7 @@ export const sendGCM = function ({ userTokens, notification, _replaceToken, _rem const userToken = userTokens.length === 1 ? userTokens[0] : null; - sender.send(message, userTokens, 5, function (err, result) { + sender.send(message, userTokens, 5, (err, result) => { if (err) { logger.debug({ msg: 'ANDROID ERROR: result of sender', result }); return; @@ -105,7 +91,7 @@ export const sendGCM = function ({ userTokens, notification, _replaceToken, _rem logger.debug({ msg: 'ANDROID: Result of sender', result }); - if (result.canonical_ids === 1 && userToken) { + if (result.canonical_ids === 1 && userToken && result.results?.[0].registration_id) { // This is an old device, token is replaced try { _replaceToken({ gcm: userToken }, { gcm: result.results[0].registration_id }); diff --git a/apps/meteor/app/push/server/logger.ts b/apps/meteor/app/push/server/logger.ts index f770aef378f6..5793949089dd 100644 --- a/apps/meteor/app/push/server/logger.ts +++ b/apps/meteor/app/push/server/logger.ts @@ -1,3 +1,3 @@ -import { Logger } from '../../../server/lib/logger/Logger'; +import { Logger } from '@rocket.chat/logger'; export const logger = new Logger('Push'); diff --git a/apps/meteor/app/push/server/methods.ts b/apps/meteor/app/push/server/methods.ts index 324656306c5e..364a16cbdb5e 100644 --- a/apps/meteor/app/push/server/methods.ts +++ b/apps/meteor/app/push/server/methods.ts @@ -1,20 +1,20 @@ -import { Accounts } from 'meteor/accounts-base'; -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; +import type { IAppsTokens } from '@rocket.chat/core-typings'; +import { AppsTokens } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { AppsTokens } from '@rocket.chat/models'; -import type { IAppsTokens } from '@rocket.chat/core-typings'; +import { Accounts } from 'meteor/accounts-base'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { _matchToken } from './push'; import { logger } from './logger'; +import { _matchToken } from './push'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'raix:push-update'(options: { id?: string; - token: string; + token: IAppsTokens['token']; authToken: string; appName: string; userId?: string; diff --git a/apps/meteor/app/push/server/push.js b/apps/meteor/app/push/server/push.ts similarity index 52% rename from apps/meteor/app/push/server/push.js rename to apps/meteor/app/push/server/push.ts index c6a6929adb66..fe08f7b0a214 100644 --- a/apps/meteor/app/push/server/push.js +++ b/apps/meteor/app/push/server/push.ts @@ -1,28 +1,75 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import _ from 'underscore'; +import type { IAppsTokens, RequiredField, Optional, IPushNotificationConfig } from '@rocket.chat/core-typings'; import { AppsTokens } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { pick } from '@rocket.chat/tools'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { settings } from '../../settings/server'; import { initAPN, sendAPN } from './apn'; +import type { PushOptions, PendingPushNotification } from './definition'; import { sendGCM } from './gcm'; import { logger } from './logger'; -import { settings } from '../../settings/server'; export const _matchToken = Match.OneOf({ apn: String }, { gcm: String }); +// This type must match the type defined in the push gateway +type GatewayNotification = { + uniqueId: string; + from: string; + title: string; + text: string; + badge?: number; + sound?: string; + notId?: number; + contentAvailable?: 1 | 0; + forceStart?: number; + topic?: string; + apn?: { + from?: string; + title?: string; + text?: string; + badge?: number; + sound?: string; + notId?: number; + category?: string; + }; + gcm?: { + from?: string; + title?: string; + text?: string; + image?: string; + style?: string; + summaryText?: string; + picture?: string; + badge?: number; + sound?: string; + notId?: number; + actions?: any[]; + }; + query?: { + userId: any; + }; + token?: IAppsTokens['token']; + tokens?: IAppsTokens['token'][]; + payload?: Record; + delayUntil?: Date; + createdAt: Date; + createdBy?: string; +}; + class PushClass { - options = {}; + options: PushOptions = { + uniqueId: '', + }; isConfigured = false; - configure(options) { - this.options = Object.assign( - { - sendTimeout: 60000, // Timeout period for notification send - }, - options, - ); + public configure(options: PushOptions): void { + this.options = { + sendTimeout: 60000, // Timeout period for notification send + ...options, + }; // https://npmjs.org/package/apn // After requesting the certificate from Apple, export your private key as @@ -46,57 +93,44 @@ class PushClass { logger.debug('Configure', this.options); if (this.options.apn) { - initAPN({ options: this.options, absoluteUrl: Meteor.absoluteUrl() }); + initAPN({ options: this.options as RequiredField, absoluteUrl: Meteor.absoluteUrl() }); } } - sendWorker(task, interval) { - logger.debug(`Send worker started, using interval: ${interval}`); - - return setInterval(() => { - try { - task(); - } catch (error) { - logger.debug(`Error while sending: ${error.message}`); - } - }, interval); - } - - _replaceToken(currentToken, newToken) { + private replaceToken(currentToken: IAppsTokens['token'], newToken: IAppsTokens['token']): void { void AppsTokens.updateMany({ token: currentToken }, { $set: { token: newToken } }); } - _removeToken(token) { + private removeToken(token: IAppsTokens['token']): void { void AppsTokens.deleteOne({ token }); } - _shouldUseGateway() { - return !!this.options.gateways && settings.get('Register_Server') && settings.get('Cloud_Service_Agree_PrivacyTerms'); + private shouldUseGateway(): boolean { + return Boolean(!!this.options.gateways && settings.get('Register_Server') && settings.get('Cloud_Service_Agree_PrivacyTerms')); } - sendNotificationNative(app, notification, countApn, countGcm) { + private sendNotificationNative(app: IAppsTokens, notification: PendingPushNotification, countApn: string[], countGcm: string[]): void { logger.debug('send to token', app.token); - if (app.token.apn) { + if ('apn' in app.token && app.token.apn) { countApn.push(app._id); // Send to APN if (this.options.apn) { - notification.topic = app.appName; - sendAPN({ userToken: app.token.apn, notification, _removeToken: this._removeToken }); + sendAPN({ userToken: app.token.apn, notification: { topic: app.appName, ...notification }, _removeToken: this.removeToken }); } - } else if (app.token.gcm) { + } else if ('gcm' in app.token && app.token.gcm) { countGcm.push(app._id); // Send to GCM // We do support multiple here - so we should construct an array // and send it bulk - Investigate limit count of id's - if (this.options.gcm && this.options.gcm.apiKey) { + if (this.options.gcm?.apiKey) { sendGCM({ userTokens: app.token.gcm, notification, - _replaceToken: this._replaceToken, - _removeToken: this._removeToken, - options: this.options, + _replaceToken: this.replaceToken, + _removeToken: this.removeToken, + options: this.options as RequiredField, }); } } else { @@ -104,7 +138,13 @@ class PushClass { } } - async sendGatewayPush(gateway, service, token, notification, tries = 0) { + private async sendGatewayPush( + gateway: string, + service: 'apn' | 'gcm', + token: string, + notification: Optional, + tries = 0, + ): Promise { notification.uniqueId = this.options.uniqueId; const options = { @@ -156,32 +196,55 @@ class PushClass { logger.log('Trying sending push to gateway again in', ms, 'milliseconds'); - return setTimeout(() => this.sendGatewayPush(gateway, service, token, notification, tries + 1), ms); + setTimeout(() => this.sendGatewayPush(gateway, service, token, notification, tries + 1), ms); } } - async sendNotificationGateway(app, notification, countApn, countGcm) { + private getGatewayNotificationData(notification: PendingPushNotification): Omit { + // Gateway accepts every attribute from the PendingPushNotification type, except for the priority and apn.topicSuffix + const { priority: _priority, apn, ...notifData } = notification; + const { topicSuffix: _topicSuffix, ...apnData } = apn || ({} as RequiredField['apn']); + + return { + ...notifData, + ...(notification.apn ? { apn: { ...apnData } } : {}), + }; + } + + private async sendNotificationGateway( + app: IAppsTokens, + notification: PendingPushNotification, + countApn: string[], + countGcm: string[], + ): Promise { + if (!this.options.gateways) { + return; + } + + const { topicSuffix = '' } = notification.apn || {}; + + const gatewayNotification = this.getGatewayNotificationData(notification); + for (const gateway of this.options.gateways) { logger.debug('send to token', app.token); - if (app.token.apn) { + if ('apn' in app.token && app.token.apn) { countApn.push(app._id); - notification.topic = app.appName; - return this.sendGatewayPush(gateway, 'apn', app.token.apn, notification); + return this.sendGatewayPush(gateway, 'apn', app.token.apn, { topic: `${app.appName}${topicSuffix}`, ...gatewayNotification }); } - if (app.token.gcm) { + if ('gcm' in app.token && app.token.gcm) { countGcm.push(app._id); - return this.sendGatewayPush(gateway, 'gcm', app.token.gcm, notification); + return this.sendGatewayPush(gateway, 'gcm', app.token.gcm, gatewayNotification); } } } - async sendNotification(notification = { badge: 0 }) { + private async sendNotification(notification: PendingPushNotification): Promise<{ apn: string[]; gcm: string[] }> { logger.debug('Sending notification', notification); - const countApn = []; - const countGcm = []; + const countApn: string[] = []; + const countGcm: string[] = []; if (notification.from !== String(notification.from)) { throw new Error('Push.send: option "from" not a string'); @@ -200,15 +263,18 @@ class PushClass { $or: [{ 'token.apn': { $exists: true } }, { 'token.gcm': { $exists: true } }], }; - await AppsTokens.find(query).forEach((app) => { + const appTokens = AppsTokens.find(query); + + for await (const app of appTokens) { logger.debug('send to token', app.token); - if (this._shouldUseGateway()) { - return this.sendNotificationGateway(app, notification, countApn, countGcm); + if (this.shouldUseGateway()) { + await this.sendNotificationGateway(app, notification, countApn, countGcm); + continue; } - return this.sendNotificationNative(app, notification, countApn, countGcm); - }); + this.sendNotificationNative(app, notification, countApn, countGcm); + } if (settings.get('Log_Level') === '2') { logger.debug(`Sent message "${notification.title}" to ${countApn.length} ios apps ${countGcm.length} android apps`); @@ -238,7 +304,7 @@ class PushClass { // This is a general function to validate that the data added to notifications // is in the correct format. If not this function will throw errors - _validateDocument(notification) { + private _validateDocument(notification: PendingPushNotification): void { // Check the general notification check(notification, { from: String, @@ -250,35 +316,19 @@ class PushClass { sound: Match.Optional(String), notId: Match.Optional(Match.Integer), contentAvailable: Match.Optional(Match.Integer), - forceStart: Match.Optional(Match.Integer), apn: Match.Optional({ - from: Match.Optional(String), - title: Match.Optional(String), - text: Match.Optional(String), - badge: Match.Optional(Match.Integer), - sound: Match.Optional(String), - notId: Match.Optional(Match.Integer), - actions: Match.Optional([Match.Any]), category: Match.Optional(String), + topicSuffix: Match.Optional(String), }), gcm: Match.Optional({ - from: Match.Optional(String), - title: Match.Optional(String), - text: Match.Optional(String), image: Match.Optional(String), style: Match.Optional(String), - summaryText: Match.Optional(String), - picture: Match.Optional(String), - badge: Match.Optional(Match.Integer), - sound: Match.Optional(String), - notId: Match.Optional(Match.Integer), }), - android_channel_id: Match.Optional(String), userId: String, payload: Match.Optional(Object), - delayUntil: Match.Optional(Date), createdAt: Date, createdBy: Match.OneOf(String, null), + priority: Match.Optional(Match.Integer), }); if (!notification.userId) { @@ -286,64 +336,47 @@ class PushClass { } } - async send(options) { - // If on the client we set the user id - on the server we need an option - // set or we default to "" as the creator of the notification - // If current user not set see if we can set it to the logged in user - // this will only run on the client if Meteor.userId is available - const currentUser = options.createdBy || ''; - - // Rig the notification object - const notification = Object.assign( - { - createdAt: new Date(), - createdBy: currentUser, - sent: false, - sending: 0, - }, - _.pick(options, 'from', 'title', 'text', 'userId'), - ); - - // Add extra - Object.assign(notification, _.pick(options, 'payload', 'badge', 'sound', 'notId', 'delayUntil', 'android_channel_id')); - - if (Match.test(options.apn, Object)) { - notification.apn = _.pick(options.apn, 'from', 'title', 'text', 'badge', 'sound', 'notId', 'category'); - } - - if (Match.test(options.gcm, Object)) { - notification.gcm = _.pick( - options.gcm, - 'image', - 'style', - 'summaryText', - 'picture', - 'from', - 'title', - 'text', - 'badge', - 'sound', - 'notId', - 'actions', - 'android_channel_id', - ); - } + private hasApnOptions(options: IPushNotificationConfig): options is RequiredField { + return Match.test(options.apn, Object); + } - if (options.contentAvailable != null) { - notification.contentAvailable = options.contentAvailable; - } + private hasGcmOptions(options: IPushNotificationConfig): options is RequiredField { + return Match.test(options.gcm, Object); + } - if (options.forceStart != null) { - notification.forceStart = options.forceStart; - } + public async send(options: IPushNotificationConfig) { + const notification: PendingPushNotification = { + createdAt: new Date(), + // createdBy is no longer used, but the gateway still expects it + createdBy: '', + sent: false, + sending: 0, + + ...pick(options, 'from', 'title', 'text', 'userId', 'payload', 'badge', 'sound', 'notId', 'priority'), + + ...(this.hasApnOptions(options) + ? { + apn: { + ...pick(options.apn, 'category', 'topicSuffix'), + }, + } + : {}), + ...(this.hasGcmOptions(options) + ? { + gcm: { + ...pick(options.gcm, 'image', 'style'), + }, + } + : {}), + }; // Validate the notification this._validateDocument(notification); try { await this.sendNotification(notification); - } catch (error) { - logger.debug(`Could not send notification id: "${notification._id}", Error: ${error.message}`); + } catch (error: any) { + logger.debug(`Could not send notification to user "${notification.userId}", Error: ${error.message}`); logger.debug(error.stack); } } diff --git a/apps/meteor/app/reactions/client/init.js b/apps/meteor/app/reactions/client/init.ts similarity index 72% rename from apps/meteor/app/reactions/client/init.js rename to apps/meteor/app/reactions/client/init.ts index 05ac899de1d9..24840b9de7cf 100644 --- a/apps/meteor/app/reactions/client/init.js +++ b/apps/meteor/app/reactions/client/init.ts @@ -1,20 +1,20 @@ import { Meteor } from 'meteor/meteor'; -import { MessageAction } from '../../ui-utils/client'; -import { messageArgs } from '../../../client/lib/utils/messageArgs'; import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; +import { messageArgs } from '../../../client/lib/utils/messageArgs'; +import { MessageAction } from '../../ui-utils/client'; import { sdk } from '../../utils/client/lib/SDKClient'; -Meteor.startup(function () { +Meteor.startup(() => { MessageAction.addButton({ id: 'reaction-message', icon: 'add-reaction', label: 'Add_Reaction', - context: ['message', 'message-mobile', 'threads', 'federated'], + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], action(event, props) { - event.stopPropagation(); const { message = messageArgs(this).msg, chat } = props; - chat?.emojiPicker.open(event.currentTarget, (emoji) => sdk.call('setReaction', `:${emoji}:`, message._id)); + event.stopPropagation(); + chat?.emojiPicker.open(event.currentTarget! as Element, (emoji) => sdk.call('setReaction', `:${emoji}:`, message._id)); }, condition({ message, user, room, subscription }) { if (!room) { @@ -29,7 +29,7 @@ Meteor.startup(function () { return false; } - if (roomCoordinator.readOnly(room._id, user) && !room.reactWhenReadOnly) { + if (roomCoordinator.readOnly(room._id, user!) && !room.reactWhenReadOnly) { return false; } const isLivechatRoom = roomCoordinator.isLivechatRoom(room.t); @@ -40,6 +40,6 @@ Meteor.startup(function () { return true; }, order: -3, - group: ['message', 'menu'], + group: 'message', }); }); diff --git a/apps/meteor/app/reactions/client/methods/setReaction.ts b/apps/meteor/app/reactions/client/methods/setReaction.ts index ddf301ff5549..a38e6c156790 100644 --- a/apps/meteor/app/reactions/client/methods/setReaction.ts +++ b/apps/meteor/app/reactions/client/methods/setReaction.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IMessage, IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { Messages, ChatRoom, Subscriptions } from '../../../models/client'; +import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; import { callbacks } from '../../../../lib/callbacks'; import { emoji } from '../../../emoji/client'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; +import { Messages, ChatRoom, Subscriptions } from '../../../models/client'; Meteor.methods({ async setReaction(reaction, messageId) { diff --git a/apps/meteor/app/reactions/server/setReaction.ts b/apps/meteor/app/reactions/server/setReaction.ts index 00f6bae48439..50ffe76810dc 100644 --- a/apps/meteor/app/reactions/server/setReaction.ts +++ b/apps/meteor/app/reactions/server/setReaction.ts @@ -1,17 +1,17 @@ -import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import { Messages, EmojiCustom, Rooms, Users } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import { Messages, EmojiCustom, Rooms, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; +import { AppEvents, Apps } from '../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../lib/callbacks'; -import { emoji } from '../../emoji/server'; -import { isTheLastMessage } from '../../lib/server'; +import { i18n } from '../../../server/lib/i18n'; import { canAccessRoomAsync } from '../../authorization/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; -import { AppEvents, Apps } from '../../../ee/server/apps/orchestrator'; -import { i18n } from '../../../server/lib/i18n'; +import { emoji } from '../../emoji/server'; +import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; const removeUserReaction = (message: IMessage, reaction: string, username: string) => { if (!message.reactions) { diff --git a/apps/meteor/app/retention-policy/server/cronPruneMessages.ts b/apps/meteor/app/retention-policy/server/cronPruneMessages.ts index d42c07e7c656..6ab3ff1de226 100644 --- a/apps/meteor/app/retention-policy/server/cronPruneMessages.ts +++ b/apps/meteor/app/retention-policy/server/cronPruneMessages.ts @@ -1,9 +1,9 @@ import type { IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; -import { Rooms } from '@rocket.chat/models'; import { cronJobs } from '@rocket.chat/cron'; +import { Rooms } from '@rocket.chat/models'; -import { settings } from '../../settings/server'; import { cleanRoomHistory } from '../../lib/server/functions/cleanRoomHistory'; +import { settings } from '../../settings/server'; const maxTimes = { c: 0, diff --git a/apps/meteor/app/search/server/events/EventService.ts b/apps/meteor/app/search/server/events/EventService.ts index fe9761e4a348..944013bb5090 100644 --- a/apps/meteor/app/search/server/events/EventService.ts +++ b/apps/meteor/app/search/server/events/EventService.ts @@ -1,5 +1,5 @@ -import { searchProviderService } from '../service'; import { SearchLogger } from '../logger/logger'; +import { searchProviderService } from '../service'; export class EventService { _pushError(name: string, value: string, _payload?: unknown) { diff --git a/apps/meteor/app/search/server/events/index.ts b/apps/meteor/app/search/server/events/index.ts index ec51c1a6c92a..fcf583d63f1b 100644 --- a/apps/meteor/app/search/server/events/index.ts +++ b/apps/meteor/app/search/server/events/index.ts @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; +import { settings } from '../../../settings/server'; import { searchProviderService } from '../service'; import { EventService } from './EventService'; diff --git a/apps/meteor/app/search/server/logger/logger.ts b/apps/meteor/app/search/server/logger/logger.ts index f002e70073d4..ac103d5da06f 100644 --- a/apps/meteor/app/search/server/logger/logger.ts +++ b/apps/meteor/app/search/server/logger/logger.ts @@ -1,3 +1,3 @@ -import { Logger } from '../../../logger/server'; +import { Logger } from '@rocket.chat/logger'; export const SearchLogger = new Logger('Search Logger'); diff --git a/apps/meteor/app/search/server/register.ts b/apps/meteor/app/search/server/register.ts index 9ef4793b0ea9..809dce9dacaf 100644 --- a/apps/meteor/app/search/server/register.ts +++ b/apps/meteor/app/search/server/register.ts @@ -1,5 +1,5 @@ -import { searchProviderService } from './service'; import { DefaultProvider } from './provider/DefaultProvider'; +import { searchProviderService } from './service'; // register provider searchProviderService.register(new DefaultProvider()); diff --git a/apps/meteor/app/search/server/search.internalService.ts b/apps/meteor/app/search/server/search.internalService.ts index 727d26a72686..97de49711284 100644 --- a/apps/meteor/app/search/server/search.internalService.ts +++ b/apps/meteor/app/search/server/search.internalService.ts @@ -2,8 +2,8 @@ import { api, ServiceClassInternal } from '@rocket.chat/core-services'; import { Users } from '@rocket.chat/models'; import { settings } from '../../settings/server'; -import { searchProviderService } from './service'; import { searchEventService } from './events'; +import { searchProviderService } from './service'; class Search extends ServiceClassInternal { protected name = 'search'; diff --git a/apps/meteor/app/search/server/service/SearchProviderService.ts b/apps/meteor/app/search/server/service/SearchProviderService.ts index db997bf3ac06..80d95fbe9a5b 100644 --- a/apps/meteor/app/search/server/service/SearchProviderService.ts +++ b/apps/meteor/app/search/server/service/SearchProviderService.ts @@ -1,7 +1,7 @@ +import { withDebouncing } from '../../../../lib/utils/highOrderFunctions'; import { settings, settingsRegistry } from '../../../settings/server'; import { SearchLogger } from '../logger/logger'; import type { SearchProvider } from '../model/SearchProvider'; -import { withDebouncing } from '../../../../lib/utils/highOrderFunctions'; export class SearchProviderService { public providers: Record = {}; diff --git a/apps/meteor/app/search/server/service/SearchResultValidationService.ts b/apps/meteor/app/search/server/service/SearchResultValidationService.ts index 4a7831d6657f..05ba3e1d4b97 100644 --- a/apps/meteor/app/search/server/service/SearchResultValidationService.ts +++ b/apps/meteor/app/search/server/service/SearchResultValidationService.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import mem from 'mem'; import type { IRoom, IUser } from '@rocket.chat/core-typings'; import { Users, Rooms } from '@rocket.chat/models'; +import mem from 'mem'; +import { Meteor } from 'meteor/meteor'; -import { SearchLogger } from '../logger/logger'; +import { isTruthy } from '../../../../lib/isTruthy'; import { canAccessRoomAsync } from '../../../authorization/server'; +import { SearchLogger } from '../logger/logger'; import type { IRawSearchResult, ISearchResult } from '../model/ISearchResult'; -import { isTruthy } from '../../../../lib/isTruthy'; export class SearchResultValidationService { private getSubscription = mem(async (rid: IRoom['_id'], uid?: IUser['_id']) => { diff --git a/apps/meteor/app/settings/client/lib/settings.ts b/apps/meteor/app/settings/client/lib/settings.ts index 0406f397bba0..0bc989e6bd7c 100644 --- a/apps/meteor/app/settings/client/lib/settings.ts +++ b/apps/meteor/app/settings/client/lib/settings.ts @@ -1,6 +1,6 @@ +import type { SettingValue } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { ReactiveDict } from 'meteor/reactive-dict'; -import type { SettingValue } from '@rocket.chat/core-typings'; import { PublicSettingsCachedCollection } from '../../../../client/lib/settings/PublicSettingsCachedCollection'; import { SettingsBase } from '../../lib/settings'; diff --git a/apps/meteor/app/settings/lib/settings.ts b/apps/meteor/app/settings/lib/settings.ts index 6581665db5a5..502b0e6cbac1 100644 --- a/apps/meteor/app/settings/lib/settings.ts +++ b/apps/meteor/app/settings/lib/settings.ts @@ -1,6 +1,6 @@ +import type { SettingValue } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import type { SettingValue } from '@rocket.chat/core-typings'; import { sdk } from '../../utils/client/lib/SDKClient'; diff --git a/apps/meteor/app/settings/server/CachedSettings.ts b/apps/meteor/app/settings/server/CachedSettings.ts index c270f3beaa3b..06cfad4a91a6 100644 --- a/apps/meteor/app/settings/server/CachedSettings.ts +++ b/apps/meteor/app/settings/server/CachedSettings.ts @@ -1,6 +1,6 @@ +import type { ISetting, SettingValue } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; import _ from 'underscore'; -import type { ISetting, SettingValue } from '@rocket.chat/core-typings'; import { SystemLogger } from '../../../server/lib/logger/system'; diff --git a/apps/meteor/app/settings/server/SettingsRegistry.ts b/apps/meteor/app/settings/server/SettingsRegistry.ts index 80379f6e62fe..5783e2946dc1 100644 --- a/apps/meteor/app/settings/server/SettingsRegistry.ts +++ b/apps/meteor/app/settings/server/SettingsRegistry.ts @@ -1,15 +1,15 @@ -import { Emitter } from '@rocket.chat/emitter'; -import { isEqual } from 'underscore'; import type { ISetting, ISettingGroup, Optional, SettingValue } from '@rocket.chat/core-typings'; import { isSettingEnterprise } from '@rocket.chat/core-typings'; +import { Emitter } from '@rocket.chat/emitter'; import type { ISettingsModel } from '@rocket.chat/model-typings'; +import { isEqual } from 'underscore'; import { SystemLogger } from '../../../server/lib/logger/system'; -import { overwriteSetting } from './functions/overwriteSetting'; -import { overrideSetting } from './functions/overrideSetting'; +import type { ICachedSettings } from './CachedSettings'; import { getSettingDefaults } from './functions/getSettingDefaults'; +import { overrideSetting } from './functions/overrideSetting'; +import { overwriteSetting } from './functions/overwriteSetting'; import { validateSetting } from './functions/validateSetting'; -import type { ICachedSettings } from './CachedSettings'; const blockedSettings = new Set(); const hiddenSettings = new Set(); diff --git a/apps/meteor/app/settings/server/index.ts b/apps/meteor/app/settings/server/index.ts index 10b796c026a5..c358a174d69c 100644 --- a/apps/meteor/app/settings/server/index.ts +++ b/apps/meteor/app/settings/server/index.ts @@ -1,10 +1,10 @@ import { Settings } from '@rocket.chat/models'; +import { use } from './Middleware'; import { SettingsRegistry } from './SettingsRegistry'; -import { initializeSettings } from './startup'; import { settings } from './cached'; +import { initializeSettings } from './startup'; import './applyMiddlewares'; -import { use } from './Middleware'; export { SettingsEvents } from './SettingsRegistry'; diff --git a/apps/meteor/app/slackbridge/client/slackbridge_import.client.js b/apps/meteor/app/slackbridge/client/slackbridge_import.client.js index 656bda9a62b1..6aeffb7bef45 100644 --- a/apps/meteor/app/slackbridge/client/slackbridge_import.client.js +++ b/apps/meteor/app/slackbridge/client/slackbridge_import.client.js @@ -1,5 +1,5 @@ import { settings } from '../../settings/client'; -import { slashCommands } from '../../utils/client'; +import { slashCommands } from '../../utils/lib/slashCommand'; settings.onload('SlackBridge_Enabled', (key, value) => { if (value) { diff --git a/apps/meteor/app/slackbridge/server/RocketAdapter.js b/apps/meteor/app/slackbridge/server/RocketAdapter.js index 5582595c0533..d0ef8157137d 100644 --- a/apps/meteor/app/slackbridge/server/RocketAdapter.js +++ b/apps/meteor/app/slackbridge/server/RocketAdapter.js @@ -1,16 +1,18 @@ import util from 'util'; -import _ from 'underscore'; -import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; -import { Random } from '@rocket.chat/random'; import { Messages, Rooms, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; -import { rocketLogger } from './logger'; import { callbacks } from '../../../lib/callbacks'; -import { settings } from '../../settings/server'; -import { createRoom, sendMessage, setUserAvatar } from '../../lib/server'; import { sleep } from '../../../lib/utils/sleep'; +import { createRoom } from '../../lib/server/functions/createRoom'; +import { sendMessage } from '../../lib/server/functions/sendMessage'; +import { setUserAvatar } from '../../lib/server/functions/setUserAvatar'; +import { settings } from '../../settings/server'; +import { rocketLogger } from './logger'; export default class RocketAdapter { constructor(slackBridge) { @@ -228,11 +230,11 @@ export default class RocketAdapter { } async getChannel(slackMessage) { - return slackMessage.channel ? this.findChannel(slackMessage.channel) || this.addChannel(slackMessage.channel) : null; + return slackMessage.channel ? (await this.findChannel(slackMessage.channel)) || this.addChannel(slackMessage.channel) : null; } async getUser(slackUser) { - return slackUser ? this.findUser(slackUser) || this.addUser(slackUser) : null; + return slackUser ? (await this.findUser(slackUser)) || this.addUser(slackUser) : null; } createRocketID(slackChannel, ts) { @@ -257,7 +259,7 @@ export default class RocketAdapter { } async getRocketUserCreator(slackChannel) { - return slackChannel.creator ? this.findUser(slackChannel.creator) || this.addUser(slackChannel.creator) : null; + return slackChannel.creator ? (await this.findUser(slackChannel.creator)) || this.addUser(slackChannel.creator) : null; } async addChannel(slackChannelID, hasRetried = false) { @@ -269,9 +271,9 @@ export default class RocketAdapter { return; } - const slackChannel = slack.slackAPI.getRoomInfo(slackChannelID); + const slackChannel = await slack.slackAPI.getRoomInfo(slackChannelID); if (slackChannel) { - const members = slack.slackAPI.getMembers(slackChannelID); + const members = await slack.slackAPI.getMembers(slackChannelID); if (!members) { rocketLogger.error('Could not fetch room members'); return; @@ -284,7 +286,7 @@ export default class RocketAdapter { await Rooms.addImportIds(slackChannel.rocketId, slackChannel.id); } else { const rocketUsers = await this.getRocketUsers(members, slackChannel); - const rocketUserCreator = this.getRocketUserCreator(slackChannel); + const rocketUserCreator = await this.getRocketUserCreator(slackChannel); if (!rocketUserCreator) { rocketLogger.error({ msg: 'Could not fetch room creator information', creator: slackChannel.creator }); @@ -294,13 +296,13 @@ export default class RocketAdapter { try { const isPrivate = slackChannel.is_private; const rocketChannel = await createRoom(isPrivate ? 'p' : 'c', slackChannel.name, rocketUserCreator.username, rocketUsers); - rocketChannel.rocketId = rocketChannel.rid; + slackChannel.rocketId = rocketChannel.rid; } catch (e) { if (!hasRetried) { rocketLogger.debug('Error adding channel from Slack. Will retry in 1s.', e.message); // If first time trying to create channel fails, could be because of multiple messages received at the same time. Try again once after 1s. await sleep(1000); - return this.findChannel(slackChannelID) || this.addChannel(slackChannelID, true); + return (await this.findChannel(slackChannelID)) || this.addChannel(slackChannelID, true); } rocketLogger.error(e); } @@ -318,7 +320,6 @@ export default class RocketAdapter { if (slackChannel.purpose && slackChannel.purpose.value && slackChannel.purpose.last_set > lastSetTopic) { roomUpdate.topic = slackChannel.purpose.value; } - await Rooms.addImportIds(slackChannel.rocketId, slackChannel.id); slack.addSlackChannel(slackChannel.rocketId, slackChannelID); } @@ -352,7 +353,7 @@ export default class RocketAdapter { return; } - const user = slack.slackAPI.getUser(slackUserID); + const user = await slack.slackAPI.getUser(slackUserID); if (user) { const rocketUserData = user; const isBot = rocketUserData.is_bot === true; @@ -463,7 +464,7 @@ export default class RocketAdapter { } } else { rocketMsgObj = { - msg: this.convertSlackMsgTxtToRocketTxtFormat(slackMessage.text), + msg: await this.convertSlackMsgTxtToRocketTxtFormat(slackMessage.text), rid: rocketChannel._id, u: { _id: rocketUser._id, diff --git a/apps/meteor/app/slackbridge/server/SlackAPI.js b/apps/meteor/app/slackbridge/server/SlackAPI.js index 404a2bf9edb3..63774024dc7e 100644 --- a/apps/meteor/app/slackbridge/server/SlackAPI.js +++ b/apps/meteor/app/slackbridge/server/SlackAPI.js @@ -13,7 +13,7 @@ export class SlackAPI { types: 'public_channel', exclude_archived: true, limit: 1000, - cursor, + ...(cursor && { cursor }), }, }); const response = await request.json(); @@ -37,7 +37,7 @@ export class SlackAPI { types: 'private_channel', exclude_archived: true, limit: 1000, - cursor, + ...(cursor && { cursor }), }, }); const response = await request.json(); @@ -77,7 +77,7 @@ export class SlackAPI { token: this.apiToken, channel: channelId, limit: MAX_MEMBERS_PER_CALL, - cursor: currentCursor, + ...(currentCursor && { cursor: currentCursor }), }, }); // eslint-disable-next-line no-await-in-loop @@ -96,7 +96,7 @@ export class SlackAPI { async react(data) { const request = await fetch('https://slack.com/api/reactions.add', { method: 'POST', - body: data, + params: data, }); const response = await request.json(); return response && request.status === 200 && response && request.ok; @@ -105,7 +105,7 @@ export class SlackAPI { async removeReaction(data) { const request = await fetch('https://slack.com/api/reactions.remove', { method: 'POST', - body: data, + params: data, }); const response = await request.json(); return response && request.status === 200 && response && request.ok; @@ -114,7 +114,7 @@ export class SlackAPI { async removeMessage(data) { const request = await fetch('https://slack.com/api/chat.delete', { method: 'POST', - body: data, + params: data, }); const response = await request.json(); return response && request.status === 200 && response && request.ok; @@ -123,7 +123,7 @@ export class SlackAPI { async sendMessage(data) { const request = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', - body: data, + params: data, }); return request.json(); } @@ -131,7 +131,7 @@ export class SlackAPI { async updateMessage(data) { const request = await fetch('https://slack.com/api/chat.update', { method: 'POST', - body: data, + params: data, }); const response = await request.json(); return response && request.status === 200 && response && request.ok; diff --git a/apps/meteor/app/slackbridge/server/SlackAdapter.js b/apps/meteor/app/slackbridge/server/SlackAdapter.js index e0f1458d8c83..d5379c082507 100644 --- a/apps/meteor/app/slackbridge/server/SlackAdapter.js +++ b/apps/meteor/app/slackbridge/server/SlackAdapter.js @@ -1,21 +1,26 @@ -import url from 'url'; import http from 'http'; import https from 'https'; +import url from 'url'; +import { Message } from '@rocket.chat/core-services'; +import { Messages, Rooms, Users, ReadReceipts } from '@rocket.chat/models'; import { RTMClient } from '@slack/rtm-api'; import { Meteor } from 'meteor/meteor'; -import { Messages, Rooms, Users } from '@rocket.chat/models'; -import { Message } from '@rocket.chat/core-services'; -import { slackLogger } from './logger'; -import { SlackAPI } from './SlackAPI'; -import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; -import { settings } from '../../settings/server'; -import { deleteMessage, updateMessage, addUserToRoom, removeUserFromRoom, unarchiveRoom, sendMessage } from '../../lib/server'; -import { archiveRoom } from '../../lib/server/functions/archiveRoom'; import { saveRoomName, saveRoomTopic } from '../../channel-settings/server'; import { FileUpload } from '../../file-upload/server'; +import { addUserToRoom } from '../../lib/server/functions/addUserToRoom'; +import { archiveRoom } from '../../lib/server/functions/archiveRoom'; +import { deleteMessage } from '../../lib/server/functions/deleteMessage'; +import { removeUserFromRoom } from '../../lib/server/functions/removeUserFromRoom'; +import { sendMessage } from '../../lib/server/functions/sendMessage'; +import { unarchiveRoom } from '../../lib/server/functions/unarchiveRoom'; +import { updateMessage } from '../../lib/server/functions/updateMessage'; import { executeSetReaction } from '../../reactions/server/setReaction'; +import { settings } from '../../settings/server'; +import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; +import { SlackAPI } from './SlackAPI'; +import { slackLogger } from './logger'; export default class SlackAdapter { constructor(slackBridge) { @@ -357,14 +362,15 @@ export default class SlackAdapter { if (rocketMsg && rocketUser) { const rocketReaction = `:${slackReactionMsg.reaction}:`; + const theReaction = (rocketMsg.reactions || {})[rocketReaction]; // If the Rocket user has already been removed, then this is an echo back from slack - if (rocketMsg.reactions) { - const theReaction = rocketMsg.reactions[rocketReaction]; - if (theReaction) { - if (theReaction.usernames.indexOf(rocketUser.username) === -1) { - return; // Reaction already removed - } + if (rocketMsg.reactions && theReaction) { + if (rocketUser.roles.includes('bot')) { + return; + } + if (theReaction.usernames.indexOf(rocketUser.username) === -1) { + return; // Reaction already removed } } else { // Reaction already removed @@ -664,6 +670,10 @@ export default class SlackAdapter { }); } + createSlackMessageId(ts, channelId) { + return `slack${channelId ? `-${channelId}` : ''}-${ts.replace(/\./g, '-')}`; + } + async postMessage(slackChannel, rocketMessage) { if (slackChannel && slackChannel.id) { let iconUrl = getUserAvatarURL(rocketMessage.u && rocketMessage.u.username); @@ -969,7 +979,7 @@ export default class SlackAdapter { async processShareMessage(rocketChannel, rocketUser, slackMessage, isImporting) { if (slackMessage.file && slackMessage.file.url_private_download !== undefined) { const details = { - message_id: `slack-${slackMessage.ts.replace(/\./g, '-')}`, + message_id: this.createSlackMessageId(slackMessage.ts), name: slackMessage.file.name, size: slackMessage.file.size, type: slackMessage.file.mimetype, @@ -1006,13 +1016,12 @@ export default class SlackAdapter { ], }; - if (!isImporting) { - await Messages.setPinnedByIdAndUserId( - `slack-${slackMessage.attachments[0].channel_id}-${slackMessage.attachments[0].ts.replace(/\./g, '-')}`, - rocketMsgObj.u, - true, - new Date(parseInt(slackMessage.ts.split('.')[0]) * 1000), - ); + if (!isImporting && slackMessage.attachments[0].channel_id && slackMessage.attachments[0].ts) { + const messageId = this.createSlackMessageId(slackMessage.attachments[0].ts, slackMessage.attachments[0].channel_id); + await Messages.setPinnedByIdAndUserId(messageId, rocketMsgObj.u, true, new Date(parseInt(slackMessage.ts.split('.')[0]) * 1000)); + if (settings.get('Message_Read_Receipt_Store_Users')) { + await ReadReceipts.setPinnedByMessageId(messageId, true); + } } return rocketMsgObj; @@ -1218,12 +1227,11 @@ export default class SlackAdapter { ], }; - await Messages.setPinnedByIdAndUserId( - `slack-${pin.channel}-${pin.message.ts.replace(/\./g, '-')}`, - msgObj.u, - true, - new Date(parseInt(pin.message.ts.split('.')[0]) * 1000), - ); + const messageId = this.createSlackMessageId(pin.message.ts, pin.channel); + await Messages.setPinnedByIdAndUserId(messageId, msgObj.u, true, new Date(parseInt(pin.message.ts.split('.')[0]) * 1000)); + if (settings.get('Message_Read_Receipt_Store_Users')) { + await ReadReceipts.setPinnedByMessageId(messageId, true); + } } } } diff --git a/apps/meteor/app/slackbridge/server/logger.ts b/apps/meteor/app/slackbridge/server/logger.ts index 4535a6af6b01..6c3586038090 100644 --- a/apps/meteor/app/slackbridge/server/logger.ts +++ b/apps/meteor/app/slackbridge/server/logger.ts @@ -1,4 +1,4 @@ -import { Logger } from '../../logger/server'; +import { Logger } from '@rocket.chat/logger'; const logger = new Logger('SlackBridge'); diff --git a/apps/meteor/app/slackbridge/server/removeChannelLinks.ts b/apps/meteor/app/slackbridge/server/removeChannelLinks.ts index 95fafe07d208..1a4e6f6a0096 100644 --- a/apps/meteor/app/slackbridge/server/removeChannelLinks.ts +++ b/apps/meteor/app/slackbridge/server/removeChannelLinks.ts @@ -1,6 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { settings } from '../../settings/server'; diff --git a/apps/meteor/app/slackbridge/server/slackbridge.js b/apps/meteor/app/slackbridge/server/slackbridge.js index d5d1c04f05db..3198b750145f 100644 --- a/apps/meteor/app/slackbridge/server/slackbridge.js +++ b/apps/meteor/app/slackbridge/server/slackbridge.js @@ -1,7 +1,7 @@ -import SlackAdapter from './SlackAdapter.js'; +import { settings } from '../../settings/server'; import RocketAdapter from './RocketAdapter.js'; +import SlackAdapter from './SlackAdapter.js'; import { classLogger, connLogger } from './logger'; -import { settings } from '../../settings/server'; /** * SlackBridge interfaces between this Rocket installation and a remote Slack installation. diff --git a/apps/meteor/app/slackbridge/server/slackbridge_import.server.js b/apps/meteor/app/slackbridge/server/slackbridge_import.server.js index 80b20076972f..2cac64433cb5 100644 --- a/apps/meteor/app/slackbridge/server/slackbridge_import.server.js +++ b/apps/meteor/app/slackbridge/server/slackbridge_import.server.js @@ -1,11 +1,11 @@ -import { Match } from 'meteor/check'; -import { Random } from '@rocket.chat/random'; import { Rooms, Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; +import { Match } from 'meteor/check'; -import { SlackBridge } from './slackbridge'; -import { msgStream } from '../../lib/server'; -import { slashCommands } from '../../utils/server'; import { i18n } from '../../../server/lib/i18n'; +import { msgStream } from '../../lib/server'; +import { slashCommands } from '../../utils/server/slashCommand'; +import { SlackBridge } from './slackbridge'; async function SlackBridgeImport({ command, params, message, userId }) { if (command !== 'slackbridge-import' || !Match.test(params, String)) { diff --git a/apps/meteor/app/slashcommand-asciiarts/client/gimme.ts b/apps/meteor/app/slashcommand-asciiarts/client/gimme.ts index 6da8948b735d..4c9d6a4e40c8 100644 --- a/apps/meteor/app/slashcommand-asciiarts/client/gimme.ts +++ b/apps/meteor/app/slashcommand-asciiarts/client/gimme.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Gimme is a named function that will replace /gimme commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/client/lenny.ts b/apps/meteor/app/slashcommand-asciiarts/client/lenny.ts index c46466e2d7c0..99eaa03b9e59 100644 --- a/apps/meteor/app/slashcommand-asciiarts/client/lenny.ts +++ b/apps/meteor/app/slashcommand-asciiarts/client/lenny.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Lenny is a named function that will replace /lenny commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/client/shrug.ts b/apps/meteor/app/slashcommand-asciiarts/client/shrug.ts index c1b1c35d370d..bc0fb300789e 100644 --- a/apps/meteor/app/slashcommand-asciiarts/client/shrug.ts +++ b/apps/meteor/app/slashcommand-asciiarts/client/shrug.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Shrug is a named function that will replace /shrug commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/client/tableflip.ts b/apps/meteor/app/slashcommand-asciiarts/client/tableflip.ts index 25f4b10694ae..0d709760fe84 100644 --- a/apps/meteor/app/slashcommand-asciiarts/client/tableflip.ts +++ b/apps/meteor/app/slashcommand-asciiarts/client/tableflip.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Tableflip is a named function that will replace /Tableflip commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/client/unflip.ts b/apps/meteor/app/slashcommand-asciiarts/client/unflip.ts index d6eab44fd7fe..a7dc0d257e78 100644 --- a/apps/meteor/app/slashcommand-asciiarts/client/unflip.ts +++ b/apps/meteor/app/slashcommand-asciiarts/client/unflip.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Unflip is a named function that will replace /unflip commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/server/gimme.ts b/apps/meteor/app/slashcommand-asciiarts/server/gimme.ts index c612ea061893..f426d6cf85c0 100644 --- a/apps/meteor/app/slashcommand-asciiarts/server/gimme.ts +++ b/apps/meteor/app/slashcommand-asciiarts/server/gimme.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Gimme is a named function that will replace /gimme commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/server/lenny.ts b/apps/meteor/app/slashcommand-asciiarts/server/lenny.ts index 90792a207161..878a10e356d4 100644 --- a/apps/meteor/app/slashcommand-asciiarts/server/lenny.ts +++ b/apps/meteor/app/slashcommand-asciiarts/server/lenny.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Lenny is a named function that will replace /lenny commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/server/shrug.ts b/apps/meteor/app/slashcommand-asciiarts/server/shrug.ts index 90a838066e2b..1240027bb38f 100644 --- a/apps/meteor/app/slashcommand-asciiarts/server/shrug.ts +++ b/apps/meteor/app/slashcommand-asciiarts/server/shrug.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Shrug is a named function that will replace /shrug commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/server/tableflip.ts b/apps/meteor/app/slashcommand-asciiarts/server/tableflip.ts index 2088f9ea19d0..34acef9805e2 100644 --- a/apps/meteor/app/slashcommand-asciiarts/server/tableflip.ts +++ b/apps/meteor/app/slashcommand-asciiarts/server/tableflip.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Tableflip is a named function that will replace /Tableflip commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommand-asciiarts/server/unflip.ts b/apps/meteor/app/slashcommand-asciiarts/server/unflip.ts index 9005eff9e6fc..689e7262eac0 100644 --- a/apps/meteor/app/slashcommand-asciiarts/server/unflip.ts +++ b/apps/meteor/app/slashcommand-asciiarts/server/unflip.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Unflip is a named function that will replace /unflip commands * @param {Object} message - The message object diff --git a/apps/meteor/app/slashcommands-archiveroom/server/server.ts b/apps/meteor/app/slashcommands-archiveroom/server/server.ts index e39fe8874eeb..46708c667678 100644 --- a/apps/meteor/app/slashcommands-archiveroom/server/server.ts +++ b/apps/meteor/app/slashcommands-archiveroom/server/server.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; +import { api } from '@rocket.chat/core-services'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { isRegisterUser } from '@rocket.chat/core-typings'; -import { api } from '@rocket.chat/core-services'; import { Users, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { settings } from '../../settings/server'; -import { archiveRoom } from '../../lib/server/functions/archiveRoom'; import { i18n } from '../../../server/lib/i18n'; +import { archiveRoom } from '../../lib/server/functions/archiveRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'archive', diff --git a/apps/meteor/app/slashcommands-create/server/server.ts b/apps/meteor/app/slashcommands-create/server/server.ts index 5cbff55fbcc1..a3c70f012fa1 100644 --- a/apps/meteor/app/slashcommands-create/server/server.ts +++ b/apps/meteor/app/slashcommands-create/server/server.ts @@ -1,12 +1,12 @@ import { api } from '@rocket.chat/core-services'; -import { Rooms } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; -import { settings } from '../../settings/server'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { i18n } from '../../../server/lib/i18n'; -import { createPrivateGroupMethod } from '../../lib/server/methods/createPrivateGroup'; import { createChannelMethod } from '../../lib/server/methods/createChannel'; +import { createPrivateGroupMethod } from '../../lib/server/methods/createPrivateGroup'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'create', diff --git a/apps/meteor/app/slashcommands-help/server/server.ts b/apps/meteor/app/slashcommands-help/server/server.ts index 0af54eab89b6..c24bfb22c6fe 100644 --- a/apps/meteor/app/slashcommands-help/server/server.ts +++ b/apps/meteor/app/slashcommands-help/server/server.ts @@ -1,10 +1,10 @@ import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { i18n } from '../../../server/lib/i18n'; import { settings } from '../../settings/server'; import { slashCommands } from '../../utils/lib/slashCommand'; -import { i18n } from '../../../server/lib/i18n'; /* * Help is a named function that will replace /help commands diff --git a/apps/meteor/app/slashcommands-hide/server/hide.ts b/apps/meteor/app/slashcommands-hide/server/hide.ts index b92ba8f24764..bc614b95e8ed 100644 --- a/apps/meteor/app/slashcommands-hide/server/hide.ts +++ b/apps/meteor/app/slashcommands-hide/server/hide.ts @@ -1,11 +1,11 @@ import { api } from '@rocket.chat/core-services'; -import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; import type { IRoom, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; -import { settings } from '../../settings/server'; -import { slashCommands } from '../../utils/server'; import { i18n } from '../../../server/lib/i18n'; import { hideRoomMethod } from '../../../server/methods/hideRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/server/slashCommand'; /* * Hide is a named function that will replace /hide commands diff --git a/apps/meteor/app/slashcommands-invite/server/server.ts b/apps/meteor/app/slashcommands-invite/server/server.ts index e195b9c5deca..de525d8c6fc6 100644 --- a/apps/meteor/app/slashcommands-invite/server/server.ts +++ b/apps/meteor/app/slashcommands-invite/server/server.ts @@ -1,11 +1,12 @@ -import type { IUser, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { api } from '@rocket.chat/core-services'; +import type { IUser, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { Subscriptions, Users } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { i18n } from '../../../server/lib/i18n'; import { addUsersToRoomMethod } from '../../lib/server/methods/addUsersToRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Invite is a named function that will replace /invite commands @@ -57,13 +58,25 @@ slashCommands.add({ }); } + const inviter = await Users.findOneById(userId); + + if (!inviter) { + throw new Meteor.Error('error-user-not-found', 'Inviter not found', { + method: 'slashcommand-invite', + }); + } + await Promise.all( usersFiltered.map(async (user) => { try { - return await addUsersToRoomMethod(userId, { - rid: message.rid, - users: [user.username || ''], - }); + return await addUsersToRoomMethod( + userId, + { + rid: message.rid, + users: [user.username || ''], + }, + inviter, + ); } catch ({ error }: any) { if (typeof error !== 'string') { return; diff --git a/apps/meteor/app/slashcommands-inviteall/server/server.ts b/apps/meteor/app/slashcommands-inviteall/server/server.ts index 5e045d39038f..9917775aca06 100644 --- a/apps/meteor/app/slashcommands-inviteall/server/server.ts +++ b/apps/meteor/app/slashcommands-inviteall/server/server.ts @@ -8,13 +8,13 @@ import type { ISubscription, SlashCommand, SlashCommandCallbackParams } from '@r import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; +import { isTruthy } from '../../../lib/isTruthy'; import { i18n } from '../../../server/lib/i18n'; -import { slashCommands } from '../../utils/lib/slashCommand'; +import { addUsersToRoomMethod } from '../../lib/server/methods/addUsersToRoom'; import { createChannelMethod } from '../../lib/server/methods/createChannel'; import { createPrivateGroupMethod } from '../../lib/server/methods/createPrivateGroup'; -import { isTruthy } from '../../../lib/isTruthy'; -import { addUsersToRoomMethod } from '../../lib/server/methods/addUsersToRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; function inviteAll(type: T): SlashCommand['callback'] { return async function inviteAll({ command, params, message, userId }: SlashCommandCallbackParams): Promise { diff --git a/apps/meteor/app/slashcommands-join/server/server.ts b/apps/meteor/app/slashcommands-join/server/server.ts index 503805ec17d7..dfe27d8d5dc4 100644 --- a/apps/meteor/app/slashcommands-join/server/server.ts +++ b/apps/meteor/app/slashcommands-join/server/server.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; import { api } from '@rocket.chat/core-services'; -import { Rooms, Subscriptions } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Rooms, Subscriptions } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { i18n } from '../../../server/lib/i18n'; import { joinRoomMethod } from '../../lib/server/methods/joinRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'join', diff --git a/apps/meteor/app/slashcommands-kick/server/server.ts b/apps/meteor/app/slashcommands-kick/server/server.ts index 322ffbd4e698..5ca6b45ec835 100644 --- a/apps/meteor/app/slashcommands-kick/server/server.ts +++ b/apps/meteor/app/slashcommands-kick/server/server.ts @@ -1,12 +1,12 @@ // Kick is a named function that will replace /kick commands import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { i18n } from '../../../server/lib/i18n'; +import { removeUserFromRoomMethod } from '../../../server/methods/removeUserFromRoom'; import { settings } from '../../settings/server'; import { slashCommands } from '../../utils/lib/slashCommand'; -import { removeUserFromRoomMethod } from '../../../server/methods/removeUserFromRoom'; -import { i18n } from '../../../server/lib/i18n'; slashCommands.add({ command: 'kick', diff --git a/apps/meteor/app/slashcommands-leave/server/leave.ts b/apps/meteor/app/slashcommands-leave/server/leave.ts index a4c5257c0dad..42dad0807246 100644 --- a/apps/meteor/app/slashcommands-leave/server/leave.ts +++ b/apps/meteor/app/slashcommands-leave/server/leave.ts @@ -1,11 +1,11 @@ -import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { api } from '@rocket.chat/core-services'; +import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { settings } from '../../settings/server'; -import { leaveRoomMethod } from '../../lib/server/methods/leaveRoom'; import { i18n } from '../../../server/lib/i18n'; +import { leaveRoomMethod } from '../../lib/server/methods/leaveRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Leave is a named function that will replace /leave commands diff --git a/apps/meteor/app/slashcommands-me/server/me.ts b/apps/meteor/app/slashcommands-me/server/me.ts index 27777dc5a718..ba6a9f8c82cc 100644 --- a/apps/meteor/app/slashcommands-me/server/me.ts +++ b/apps/meteor/app/slashcommands-me/server/me.ts @@ -1,7 +1,7 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Me is a named function that will replace /me commands diff --git a/apps/meteor/app/slashcommands-msg/server/server.ts b/apps/meteor/app/slashcommands-msg/server/server.ts index 62fe80b606e2..c6a244b80207 100644 --- a/apps/meteor/app/slashcommands-msg/server/server.ts +++ b/apps/meteor/app/slashcommands-msg/server/server.ts @@ -1,13 +1,13 @@ -import { Random } from '@rocket.chat/random'; import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; +import { Random } from '@rocket.chat/random'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { settings } from '../../settings/server'; -import { executeSendMessage } from '../../lib/server/methods/sendMessage'; -import { createDirectMessage } from '../../../server/methods/createDirectMessage'; import { i18n } from '../../../server/lib/i18n'; +import { createDirectMessage } from '../../../server/methods/createDirectMessage'; +import { executeSendMessage } from '../../lib/server/methods/sendMessage'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Msg is a named function that will replace /msg commands diff --git a/apps/meteor/app/slashcommands-mute/server/mute.ts b/apps/meteor/app/slashcommands-mute/server/mute.ts index 5c81f9118564..03ce960496da 100644 --- a/apps/meteor/app/slashcommands-mute/server/mute.ts +++ b/apps/meteor/app/slashcommands-mute/server/mute.ts @@ -1,11 +1,11 @@ import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { settings } from '../../settings/server'; -import { muteUserInRoom } from '../../../server/methods/muteUserInRoom'; import { i18n } from '../../../server/lib/i18n'; +import { muteUserInRoom } from '../../../server/methods/muteUserInRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Mute is a named function that will replace /mute commands diff --git a/apps/meteor/app/slashcommands-mute/server/unmute.ts b/apps/meteor/app/slashcommands-mute/server/unmute.ts index 5862e7450f76..25c0956d49e3 100644 --- a/apps/meteor/app/slashcommands-mute/server/unmute.ts +++ b/apps/meteor/app/slashcommands-mute/server/unmute.ts @@ -1,11 +1,11 @@ import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { settings } from '../../settings/server'; -import { unmuteUserInRoom } from '../../../server/methods/unmuteUserInRoom'; import { i18n } from '../../../server/lib/i18n'; +import { unmuteUserInRoom } from '../../../server/methods/unmuteUserInRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; /* * Unmute is a named function that will replace /unmute commands diff --git a/apps/meteor/app/slashcommands-open/client/client.ts b/apps/meteor/app/slashcommands-open/client/client.ts index 696806619965..987df9599761 100644 --- a/apps/meteor/app/slashcommands-open/client/client.ts +++ b/apps/meteor/app/slashcommands-open/client/client.ts @@ -2,10 +2,10 @@ import type { RoomType, ISubscription, SlashCommandCallbackParams } from '@rocke import type { Mongo } from 'meteor/mongo'; import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; -import { slashCommands } from '../../utils/lib/slashCommand'; +import { router } from '../../../client/providers/RouterProvider'; import { Subscriptions, ChatSubscription } from '../../models/client'; import { sdk } from '../../utils/client/lib/SDKClient'; -import { router } from '../../../client/providers/RouterProvider'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'open', diff --git a/apps/meteor/app/slashcommands-status/client/status.ts b/apps/meteor/app/slashcommands-status/client/status.ts index d97bbd507d6d..9136ef8f586f 100644 --- a/apps/meteor/app/slashcommands-status/client/status.ts +++ b/apps/meteor/app/slashcommands-status/client/status.ts @@ -1,8 +1,8 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { dispatchToastMessage } from '../../../client/lib/toast'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'status', diff --git a/apps/meteor/app/slashcommands-status/server/status.ts b/apps/meteor/app/slashcommands-status/server/status.ts index abfec7e92ea4..72d92afaf3f2 100644 --- a/apps/meteor/app/slashcommands-status/server/status.ts +++ b/apps/meteor/app/slashcommands-status/server/status.ts @@ -1,11 +1,11 @@ import { api } from '@rocket.chat/core-services'; -import { Users } from '@rocket.chat/models'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; -import { slashCommands } from '../../utils/lib/slashCommand'; +import { i18n } from '../../../server/lib/i18n'; import { settings } from '../../settings/server'; import { setUserStatusMethod } from '../../user-status/server/methods/setUserStatus'; -import { i18n } from '../../../server/lib/i18n'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'status', diff --git a/apps/meteor/app/slashcommands-topic/client/topic.ts b/apps/meteor/app/slashcommands-topic/client/topic.ts index ae33509c8e7f..f5f5ed58bb0f 100644 --- a/apps/meteor/app/slashcommands-topic/client/topic.ts +++ b/apps/meteor/app/slashcommands-topic/client/topic.ts @@ -1,11 +1,11 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { ChatRoom } from '../../models/client/models/ChatRoom'; +import { dispatchToastMessage } from '../../../client/lib/toast'; import { callbacks } from '../../../lib/callbacks'; import { hasPermission } from '../../authorization/client'; -import { dispatchToastMessage } from '../../../client/lib/toast'; +import { ChatRoom } from '../../models/client/models/ChatRoom'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'topic', diff --git a/apps/meteor/app/slashcommands-topic/server/topic.ts b/apps/meteor/app/slashcommands-topic/server/topic.ts index 47151ab003f6..24fd51d5f509 100644 --- a/apps/meteor/app/slashcommands-topic/server/topic.ts +++ b/apps/meteor/app/slashcommands-topic/server/topic.ts @@ -1,8 +1,8 @@ import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../utils/lib/slashCommand'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { saveRoomSettings } from '../../channel-settings/server/methods/saveRoomSettings'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'topic', diff --git a/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts b/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts index 3a033cec485a..9c6a68b31155 100644 --- a/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts +++ b/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts @@ -1,15 +1,15 @@ -import { Meteor } from 'meteor/meteor'; import { api } from '@rocket.chat/core-services'; import { isRegisterUser } from '@rocket.chat/core-typings'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { Users, Rooms } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../utils/lib/slashCommand'; -import { settings } from '../../settings/server'; -import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../definition/IRoomTypeConfig'; -import { unarchiveRoom } from '../../lib/server'; import { i18n } from '../../../server/lib/i18n'; +import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; +import { unarchiveRoom } from '../../lib/server/functions/unarchiveRoom'; +import { settings } from '../../settings/server'; +import { slashCommands } from '../../utils/lib/slashCommand'; slashCommands.add({ command: 'unarchive', diff --git a/apps/meteor/app/smarsh-connector/server/functions/generateEml.ts b/apps/meteor/app/smarsh-connector/server/functions/generateEml.ts index 6db61d7519db..f2b6ff355730 100644 --- a/apps/meteor/app/smarsh-connector/server/functions/generateEml.ts +++ b/apps/meteor/app/smarsh-connector/server/functions/generateEml.ts @@ -1,11 +1,11 @@ +import { Messages, SmarshHistory, Users, Rooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import moment from 'moment'; -import { Messages, SmarshHistory, Users, Rooms } from '@rocket.chat/models'; +import 'moment-timezone'; +import { i18n } from '../../../../server/lib/i18n'; import { settings } from '../../../settings/server'; import { MessageTypes } from '../../../ui-utils/server'; -import 'moment-timezone'; -import { i18n } from '../../../../server/lib/i18n'; import { sendEmail } from './sendEmail'; const start = diff --git a/apps/meteor/app/smarsh-connector/server/startup.ts b/apps/meteor/app/smarsh-connector/server/startup.ts index 38d156b5b739..f757dea7510b 100644 --- a/apps/meteor/app/smarsh-connector/server/startup.ts +++ b/apps/meteor/app/smarsh-connector/server/startup.ts @@ -1,8 +1,8 @@ import { cronJobs } from '@rocket.chat/cron'; +import { smarshIntervalValuesToCronMap } from '../../../server/settings/smarsh'; import { settings } from '../../settings/server'; import { generateEml } from './functions/generateEml'; -import { smarshIntervalValuesToCronMap } from '../../../server/settings/smarsh'; const smarshJobName = 'Smarsh EML Connector'; diff --git a/apps/meteor/app/statistics/server/functions/getStatistics.ts b/apps/meteor/app/statistics/server/functions/getStatistics.ts index 4f4cefc64a9f..58e3f0ca8474 100644 --- a/apps/meteor/app/statistics/server/functions/getStatistics.ts +++ b/apps/meteor/app/statistics/server/functions/getStatistics.ts @@ -1,6 +1,6 @@ -import type { FindOptions, SchemaMember } from 'mongodb'; import type { IStats } from '@rocket.chat/core-typings'; import { Statistics } from '@rocket.chat/models'; +import type { FindOptions, SchemaMember } from 'mongodb'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/statistics/server/functions/otrStats.ts b/apps/meteor/app/statistics/server/functions/otrStats.ts index 6553cae42c05..f936f8d213f7 100644 --- a/apps/meteor/app/statistics/server/functions/otrStats.ts +++ b/apps/meteor/app/statistics/server/functions/otrStats.ts @@ -1,7 +1,7 @@ import { Rooms } from '@rocket.chat/models'; -import { updateCounter } from './updateStatsCounter'; import telemetryEvent from '../lib/telemetryEvents'; +import { updateCounter } from './updateStatsCounter'; type otrDataType = { rid: string }; diff --git a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts index 370bb3774e20..b3aa68337106 100644 --- a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts +++ b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts @@ -1,16 +1,16 @@ -import { Meteor } from 'meteor/meteor'; -import UAParser from 'ua-parser-js'; -import mem from 'mem'; import type { ISession, ISessionDevice, ISocketConnectionLogged, IUser } from '@rocket.chat/core-typings'; -import { Sessions, Users } from '@rocket.chat/models'; import { cronJobs } from '@rocket.chat/cron'; +import { Logger } from '@rocket.chat/logger'; +import { Sessions, Users } from '@rocket.chat/models'; +import mem from 'mem'; +import { Meteor } from 'meteor/meteor'; +import UAParser from 'ua-parser-js'; -import { UAParserMobile, UAParserDesktop } from './UAParserCustom'; -import { aggregates } from '../../../../server/models/raw/Sessions'; -import { Logger } from '../../../../server/lib/logger/Logger'; import { getMostImportantRole } from '../../../../lib/roles/getMostImportantRole'; -import { sauEvents } from '../../../../server/services/sauMonitor/events'; import { getClientAddress } from '../../../../server/lib/getClientAddress'; +import { aggregates } from '../../../../server/models/raw/Sessions'; +import { sauEvents } from '../../../../server/services/sauMonitor/events'; +import { UAParserMobile, UAParserDesktop } from './UAParserCustom'; type DateObj = { day: number; month: number; year: number }; diff --git a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js index e44af22aa166..6337b287506a 100644 --- a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js +++ b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js @@ -1,7 +1,7 @@ import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import { Apps } from '../../../../ee/server/apps'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; export function getAppsStatistics() { return { diff --git a/apps/meteor/app/statistics/server/lib/getServicesStatistics.ts b/apps/meteor/app/statistics/server/lib/getServicesStatistics.ts index ff7e6b544745..9e50611b3d75 100644 --- a/apps/meteor/app/statistics/server/lib/getServicesStatistics.ts +++ b/apps/meteor/app/statistics/server/lib/getServicesStatistics.ts @@ -1,5 +1,5 @@ -import { MongoInternals } from 'meteor/mongo'; import { Users } from '@rocket.chat/models'; +import { MongoInternals } from 'meteor/mongo'; import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 47c0b5b22557..8cfe45b42232 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -1,8 +1,9 @@ -import os from 'os'; import { log } from 'console'; +import os from 'os'; -import { MongoInternals } from 'meteor/mongo'; +import { Analytics, Team, VideoConf } from '@rocket.chat/core-services'; import type { IRoom, IStats } from '@rocket.chat/core-typings'; +import { UserStatus } from '@rocket.chat/core-typings'; import { NotificationQueue, Rooms, @@ -24,21 +25,21 @@ import { Subscriptions, Users, } from '@rocket.chat/models'; -import { Analytics, Team, VideoConf } from '@rocket.chat/core-services'; -import { UserStatus } from '@rocket.chat/core-typings'; +import { MongoInternals } from 'meteor/mongo'; -import { settings } from '../../../settings/server'; -import { Info, getMongoInfo } from '../../../utils/server'; +import { getStatistics as getEnterpriseStatistics } from '../../../../ee/app/license/server'; +import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; +import { isRunningMs } from '../../../../server/lib/isRunningMs'; import { getControl } from '../../../../server/lib/migrations'; +import { getSettingsStatistics } from '../../../../server/lib/statistics/getSettingsStatistics'; +import { getMatrixFederationStatistics } from '../../../../server/services/federation/infrastructure/rocket-chat/adapters/Statistics'; import { getStatistics as federationGetStatistics } from '../../../federation/server/functions/dashboard'; -import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; +import { settings } from '../../../settings/server'; +import { Info } from '../../../utils/rocketchat.info'; +import { getMongoInfo } from '../../../utils/server/functions/getMongoInfo'; import { getAppsStatistics } from './getAppsStatistics'; import { getImporterStatistics } from './getImporterStatistics'; import { getServicesStatistics } from './getServicesStatistics'; -import { getStatistics as getEnterpriseStatistics } from '../../../../ee/app/license/server'; -import { getSettingsStatistics } from '../../../../server/lib/statistics/getSettingsStatistics'; -import { isRunningMs } from '../../../../server/lib/isRunningMs'; -import { getMatrixFederationStatistics } from '../../../../server/services/federation/infrastructure/rocket-chat/adapters/Statistics'; const wizardFields = ['Organization_Type', 'Industry', 'Size', 'Country', 'Language', 'Server_Type', 'Register_Server']; @@ -121,11 +122,11 @@ export const statistics = { statistics.totalThreads = await Messages.countThreads(); // livechat visitors - statistics.totalLivechatVisitors = await LivechatVisitors.col.estimatedDocumentCount(); + statistics.totalLivechatVisitors = await LivechatVisitors.estimatedDocumentCount(); // livechat agents statistics.totalLivechatAgents = await Users.countAgents(); - statistics.totalLivechatManagers = await Users.col.countDocuments({ roles: 'livechat-manager' }); + statistics.totalLivechatManagers = await Users.countDocuments({ roles: 'livechat-manager' }); // livechat enabled statistics.livechatEnabled = settings.get('Livechat_enabled'); @@ -146,14 +147,14 @@ export const statistics = { // Number of departments statsPms.push( - LivechatDepartment.col.count().then((count) => { + LivechatDepartment.estimatedDocumentCount().then((count) => { statistics.departments = count; }), ); // Number of archived departments statsPms.push( - LivechatDepartment.col.countDocuments({ archived: true }).then((count) => { + LivechatDepartment.countArchived().then((count) => { statistics.archivedDepartments = count; }), ); diff --git a/apps/meteor/app/statistics/server/startup/monitor.ts b/apps/meteor/app/statistics/server/startup/monitor.ts index 1745e1a6855b..bae8e36ce541 100644 --- a/apps/meteor/app/statistics/server/startup/monitor.ts +++ b/apps/meteor/app/statistics/server/startup/monitor.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { SAUMonitorClass } from '../lib/SAUMonitor'; import { settings } from '../../../settings/server'; +import { SAUMonitorClass } from '../lib/SAUMonitor'; const SAUMonitor = new SAUMonitorClass(); diff --git a/apps/meteor/app/theme/client/imports/components/emoji.css b/apps/meteor/app/theme/client/imports/components/emoji.css index 4d7f19f4acb6..5180c05c5f5c 100644 --- a/apps/meteor/app/theme/client/imports/components/emoji.css +++ b/apps/meteor/app/theme/client/imports/components/emoji.css @@ -4,8 +4,8 @@ display: inline-block; overflow: hidden; - width: 22px; - height: 22px; + width: 1.375rem; + height: 1.375rem; margin: 0 0.15em; vertical-align: middle; diff --git a/apps/meteor/app/theme/client/imports/general/base_old.css b/apps/meteor/app/theme/client/imports/general/base_old.css index d5e0372fb626..7f1ede6067fc 100644 --- a/apps/meteor/app/theme/client/imports/general/base_old.css +++ b/apps/meteor/app/theme/client/imports/general/base_old.css @@ -988,10 +988,6 @@ font-size: 12px; font-weight: 300; } - - & div.switch-language { - margin-top: 20px; - } } & .share { @@ -1106,9 +1102,9 @@ } .rc-old .code-colors { - color: #333333; - border-color: #cccccc; - background-color: #f8f8f8; + color: var(--rcx-color-font-default, #1f2329); + border-color: var(--rcx-color-stroke-extra-light, #ebecef); + background-color: var(--rcx-color-surface-neutral, #e4e7ea); } .rc-old .powered-by { diff --git a/apps/meteor/app/theme/client/imports/general/reset.css b/apps/meteor/app/theme/client/imports/general/reset.css index cc8a24b549fd..ad5cbd9cddf2 100644 --- a/apps/meteor/app/theme/client/imports/general/reset.css +++ b/apps/meteor/app/theme/client/imports/general/reset.css @@ -92,8 +92,6 @@ video { border: 0 solid; - font-size: 100%; - &::after, &::before { border: 0 solid; diff --git a/apps/meteor/app/theme/client/imports/general/variables.css b/apps/meteor/app/theme/client/imports/general/variables.css index f50fc614c4bb..e8e06ac68f42 100644 --- a/apps/meteor/app/theme/client/imports/general/variables.css +++ b/apps/meteor/app/theme/client/imports/general/variables.css @@ -205,9 +205,9 @@ /* * Sidebar */ - --sidebar-width: 280px; - --sidebar-md-width: 320px; - --sidebar-lg-width: 336px; + --sidebar-width: 17.5rem; + --sidebar-md-width: 20rem; + --sidebar-lg-width: 21rem; --sidebar-small-width: 90%; --sidebar-background-hover: var(--rc-color-primary-dark); --sidebar-background-light: var(--rc-color-primary-lightest); diff --git a/apps/meteor/app/theme/client/index.ts b/apps/meteor/app/theme/client/index.ts index f518de00b591..74f07de1b081 100644 --- a/apps/meteor/app/theme/client/index.ts +++ b/apps/meteor/app/theme/client/index.ts @@ -1,5 +1 @@ import './main.css'; -import './vendor/photoswipe.css'; -import './vendor/fontello/css/fontello.css'; -import './rocketchat.font.css'; -import './mentionLink.css'; diff --git a/apps/meteor/app/theme/client/main.css b/apps/meteor/app/theme/client/main.css index ce5b0cc78300..6c0a20d844bb 100644 --- a/apps/meteor/app/theme/client/main.css +++ b/apps/meteor/app/theme/client/main.css @@ -21,3 +21,8 @@ /* Legacy theming */ @import 'imports/general/theme_old.css'; +@import './vendor/photoswipe.css'; +@import './vendor/fontello/css/fontello.css'; +@import './rocketchat.font.css'; +@import './mentionLink.css'; +@import '../../../node_modules/@rocket.chat/fuselage/dist/fuselage.css'; diff --git a/apps/meteor/app/theme/server/server.ts b/apps/meteor/app/theme/server/server.ts index 5003442b352c..d0342df87897 100644 --- a/apps/meteor/app/theme/server/server.ts +++ b/apps/meteor/app/theme/server/server.ts @@ -1,7 +1,7 @@ import crypto from 'crypto'; -import { Meteor } from 'meteor/meteor'; import { Settings } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; import { settings } from '../../settings/server'; diff --git a/apps/meteor/app/threads/client/flextab/threadlist.tsx b/apps/meteor/app/threads/client/flextab/threadlist.tsx deleted file mode 100644 index b1ca3b7b7a61..000000000000 --- a/apps/meteor/app/threads/client/flextab/threadlist.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { LazyExoticComponent, FC, ReactNode } from 'react'; -import React, { useMemo, lazy } from 'react'; -import type { BadgeProps } from '@rocket.chat/fuselage'; -import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; -import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useSetting } from '@rocket.chat/ui-contexts'; -import { HeaderToolboxAction, HeaderToolboxActionBadge } from '@rocket.chat/ui-client'; - -import { addAction } from '../../../../client/views/room/lib/Toolbox'; - -const getVariant = (tunreadUser: number, tunreadGroup: number): BadgeProps['variant'] => { - if (tunreadUser > 0) { - return 'danger'; - } - if (tunreadGroup > 0) { - return 'warning'; - } - return 'primary'; -}; - -const template = lazy(() => import('../../../../client/views/room/contextualBar/Threads')) as LazyExoticComponent; - -addAction('thread', (options) => { - const room = options.room as unknown as ISubscription & IRoom; - const federated = isRoomFederated(room); - const threadsEnabled = useSetting('Threads_enabled'); - return useMemo( - () => - threadsEnabled - ? { - groups: ['channel', 'group', 'direct', 'direct_multiple', 'team'], - id: 'thread', - full: true, - title: 'Threads', - icon: 'thread', - template, - ...(federated && { - 'data-tooltip': 'Threads_unavailable_for_federation', - 'disabled': true, - }), - renderAction: (props): ReactNode => { - const tunread = room.tunread?.length || 0; - const tunreadUser = room.tunreadUser?.length || 0; - const tunreadGroup = room.tunreadGroup?.length || 0; - const unread = tunread > 99 ? '99+' : tunread; - const variant = getVariant(tunreadUser, tunreadGroup); - return ( - - {!!unread && {unread}} - - ); - }, - order: 2, - } - : null, - [threadsEnabled, room.tunread?.length, room.tunreadUser?.length, room.tunreadGroup?.length, federated], - ); -}); diff --git a/apps/meteor/app/threads/client/index.ts b/apps/meteor/app/threads/client/index.ts index 4dad7ebd4846..160fd43a774b 100644 --- a/apps/meteor/app/threads/client/index.ts +++ b/apps/meteor/app/threads/client/index.ts @@ -1,4 +1,3 @@ -import './flextab/threadlist'; import './messageAction/follow'; import './messageAction/unfollow'; import './messageAction/replyInThread'; diff --git a/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts b/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts index d06ddca9ffc8..41a55aefc5cf 100644 --- a/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts +++ b/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { escapeHTML } from '@rocket.chat/string-helpers'; import type { IMessage } from '@rocket.chat/core-typings'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; +import { emojiParser } from '../../../emoji/client/emojiParser.js'; import { filterMarkdown } from '../../../markdown/lib/markdown'; +import { MentionsParser } from '../../../mentions/lib/MentionsParser'; import { Users } from '../../../models/client'; import { settings } from '../../../settings/client'; -import { MentionsParser } from '../../../mentions/lib/MentionsParser'; -import { emojiParser } from '../../../emoji/client/emojiParser.js'; export function normalizeThreadTitle({ ...message }: Readonly) { if (message.msg) { diff --git a/apps/meteor/app/threads/client/messageAction/follow.ts b/apps/meteor/app/threads/client/messageAction/follow.ts index c3d2023d089d..b4ca1e63e9f9 100644 --- a/apps/meteor/app/threads/client/messageAction/follow.ts +++ b/apps/meteor/app/threads/client/messageAction/follow.ts @@ -1,15 +1,15 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; +import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; +import { dispatchToastMessage } from '../../../../client/lib/toast'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { Messages } from '../../../models/client'; import { settings } from '../../../settings/client'; import { MessageAction } from '../../../ui-utils/client'; -import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; -import { dispatchToastMessage } from '../../../../client/lib/toast'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; import { t } from '../../../utils/lib/i18n'; -Meteor.startup(function () { +Meteor.startup(() => { Tracker.autorun(() => { if (!settings.get('Threads_enabled')) { return MessageAction.removeButton('follow-message'); @@ -18,7 +18,8 @@ Meteor.startup(function () { id: 'follow-message', icon: 'bell', label: 'Follow_message', - context: ['message', 'message-mobile', 'threads', 'federated'], + type: 'interaction', + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], async action(_, { message }) { if (!message) { return; @@ -44,7 +45,7 @@ Meteor.startup(function () { } return user?._id ? !replies.includes(user._id) : false; }, - order: 2, + order: 1, group: 'menu', }); }); diff --git a/apps/meteor/app/threads/client/messageAction/replyInThread.ts b/apps/meteor/app/threads/client/messageAction/replyInThread.ts index 632fbc426e4f..03f6606a2073 100644 --- a/apps/meteor/app/threads/client/messageAction/replyInThread.ts +++ b/apps/meteor/app/threads/client/messageAction/replyInThread.ts @@ -1,13 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { settings } from '../../../settings/client'; -import { MessageAction } from '../../../ui-utils/client'; -import { messageArgs } from '../../../../client/lib/utils/messageArgs'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; +import { messageArgs } from '../../../../client/lib/utils/messageArgs'; import { router } from '../../../../client/providers/RouterProvider'; +import { settings } from '../../../settings/client'; +import { MessageAction } from '../../../ui-utils/client'; -Meteor.startup(function () { +Meteor.startup(() => { Tracker.autorun(() => { if (!settings.get('Threads_enabled')) { return MessageAction.removeButton('reply-in-thread'); @@ -16,7 +16,7 @@ Meteor.startup(function () { id: 'reply-in-thread', icon: 'thread', label: 'Reply_in_thread', - context: ['message', 'message-mobile'], + context: ['message', 'message-mobile', 'federated', 'videoconf'], action(e, props) { const { message = messageArgs(this).msg } = props; e.stopPropagation(); @@ -37,7 +37,7 @@ Meteor.startup(function () { return Boolean(subscription); }, order: -1, - group: ['message', 'menu'], + group: 'message', }); }); }); diff --git a/apps/meteor/app/threads/client/messageAction/unfollow.ts b/apps/meteor/app/threads/client/messageAction/unfollow.ts index f68b35f09392..853e2adf535f 100644 --- a/apps/meteor/app/threads/client/messageAction/unfollow.ts +++ b/apps/meteor/app/threads/client/messageAction/unfollow.ts @@ -1,14 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; +import { dispatchToastMessage } from '../../../../client/lib/toast'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { Messages } from '../../../models/client'; import { settings } from '../../../settings/client'; import { MessageAction } from '../../../ui-utils/client'; -import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; -import { dispatchToastMessage } from '../../../../client/lib/toast'; import { t } from '../../../utils/lib/i18n'; -Meteor.startup(function () { +Meteor.startup(() => { Tracker.autorun(() => { if (!settings.get('Threads_enabled')) { return MessageAction.removeButton('unfollow-message'); @@ -17,7 +17,8 @@ Meteor.startup(function () { id: 'unfollow-message', icon: 'bell-off', label: 'Unfollow_message', - context: ['message', 'message-mobile', 'threads', 'federated'], + type: 'interaction', + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], async action(_, { message }) { if (!message) { return; diff --git a/apps/meteor/app/threads/server/functions.ts b/apps/meteor/app/threads/server/functions.ts index db87a8cc62a9..e79eb72ba0c5 100644 --- a/apps/meteor/app/threads/server/functions.ts +++ b/apps/meteor/app/threads/server/functions.ts @@ -1,6 +1,6 @@ -import { Messages, Subscriptions } from '@rocket.chat/models'; import type { IMessage } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; +import { Messages, Subscriptions, ReadReceipts } from '@rocket.chat/models'; import { getMentions } from '../../lib/server/lib/notifyUsersOnMessage'; @@ -21,6 +21,7 @@ export async function reply({ tmid }: { tmid?: string }, message: IMessage, pare ]; await Messages.updateRepliesByThreadId(tmid, addToReplies, ts); + await ReadReceipts.setAsThreadById(tmid); const replies = await Messages.getThreadFollowsByThreadId(tmid); diff --git a/apps/meteor/app/threads/server/hooks/aftersavemessage.ts b/apps/meteor/app/threads/server/hooks/aftersavemessage.ts index 9216f244d2c0..6af3b2eedb7e 100644 --- a/apps/meteor/app/threads/server/hooks/aftersavemessage.ts +++ b/apps/meteor/app/threads/server/hooks/aftersavemessage.ts @@ -1,13 +1,13 @@ -import { Meteor } from 'meteor/meteor'; -import { Messages } from '@rocket.chat/models'; import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; +import { Messages } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { settings } from '../../../settings/server'; -import { reply } from '../functions'; import { updateThreadUsersSubscriptions, getMentions } from '../../../lib/server/lib/notifyUsersOnMessage'; import { sendMessageNotifications } from '../../../lib/server/lib/sendNotificationsOnMessage'; +import { settings } from '../../../settings/server'; +import { reply } from '../functions'; async function notifyUsersOnReply(message: IMessage, replies: string[], room: IRoom) { // skips this callback if the message was edited @@ -65,15 +65,15 @@ export async function processThreads(message: IMessage, room: IRoom) { return message; } -Meteor.startup(function () { - settings.watch('Threads_enabled', function (value) { +Meteor.startup(() => { + settings.watch('Threads_enabled', (value) => { if (!value) { callbacks.remove('afterSaveMessage', 'threads-after-save-message'); return; } callbacks.add( 'afterSaveMessage', - async function (message, room) { + async (message, room) => { return processThreads(message, room); }, callbacks.priority.LOW, diff --git a/apps/meteor/app/threads/server/methods/followMessage.ts b/apps/meteor/app/threads/server/methods/followMessage.ts index 18eb795c31c5..cede3dda33a7 100644 --- a/apps/meteor/app/threads/server/methods/followMessage.ts +++ b/apps/meteor/app/threads/server/methods/followMessage.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { IMessage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Messages } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; +import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; -import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { follow } from '../functions'; -import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/threads/server/methods/getThreadMessages.ts b/apps/meteor/app/threads/server/methods/getThreadMessages.ts index c6610718b7a6..d6c2d65ff4d4 100644 --- a/apps/meteor/app/threads/server/methods/getThreadMessages.ts +++ b/apps/meteor/app/threads/server/methods/getThreadMessages.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import type { IMessage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Messages, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; import { canAccessRoomAsync } from '../../../authorization/server'; diff --git a/apps/meteor/app/threads/server/methods/getThreadsList.ts b/apps/meteor/app/threads/server/methods/getThreadsList.ts index dfcb310477e1..5a33dc16280b 100644 --- a/apps/meteor/app/threads/server/methods/getThreadsList.ts +++ b/apps/meteor/app/threads/server/methods/getThreadsList.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { Messages, Rooms } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { canAccessRoomAsync } from '../../../authorization/server'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/threads/server/methods/unfollowMessage.ts b/apps/meteor/app/threads/server/methods/unfollowMessage.ts index d2b337634bf1..c5dad1233173 100644 --- a/apps/meteor/app/threads/server/methods/unfollowMessage.ts +++ b/apps/meteor/app/threads/server/methods/unfollowMessage.ts @@ -1,14 +1,14 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; import type { IMessage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Messages } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; +import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; -import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { unfollow } from '../functions'; -import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/token-login/server/login_token_server.js b/apps/meteor/app/token-login/server/login_token_server.js index bc60c083b11d..a7969b1495c0 100644 --- a/apps/meteor/app/token-login/server/login_token_server.js +++ b/apps/meteor/app/token-login/server/login_token_server.js @@ -1,8 +1,8 @@ -import { check } from 'meteor/check'; -import { Accounts } from 'meteor/accounts-base'; import { Users } from '@rocket.chat/models'; +import { Accounts } from 'meteor/accounts-base'; +import { check } from 'meteor/check'; -Accounts.registerLoginHandler('login-token', async function (result) { +Accounts.registerLoginHandler('login-token', async (result) => { if (!result.loginToken) { return; } diff --git a/apps/meteor/app/tokenpass/client/lib.ts b/apps/meteor/app/tokenpass/client/lib.ts index 90164c81f3f8..c8c1daf1cd60 100644 --- a/apps/meteor/app/tokenpass/client/lib.ts +++ b/apps/meteor/app/tokenpass/client/lib.ts @@ -1,9 +1,9 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/client'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; +import { settings } from '../../settings/client'; const config: OauthConfig = { serverURL: '', @@ -23,8 +23,8 @@ const config: OauthConfig = { const Tokenpass = new CustomOAuth('tokenpass', config); -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { if (settings.get('API_Tokenpass_URL')) { config.serverURL = settings.get('API_Tokenpass_URL'); Tokenpass.configure(config); diff --git a/apps/meteor/app/tokenpass/server/lib.ts b/apps/meteor/app/tokenpass/server/lib.ts index 1ced83aa300d..32087ff60437 100644 --- a/apps/meteor/app/tokenpass/server/lib.ts +++ b/apps/meteor/app/tokenpass/server/lib.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; import type { OauthConfig } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../settings/server'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { settings } from '../../settings/server'; const config: OauthConfig = { serverURL: '', @@ -22,8 +22,8 @@ const config: OauthConfig = { const Tokenpass = new CustomOAuth('tokenpass', config); -Meteor.startup(function () { - settings.watch('API_Tokenpass_URL', function (value) { +Meteor.startup(() => { + settings.watch('API_Tokenpass_URL', (value) => { config.serverURL = value; Tokenpass.configure(config); }); diff --git a/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts b/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts index f9700100cc0e..9c970ccf697b 100644 --- a/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts +++ b/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts @@ -1,17 +1,18 @@ +import { Emitter } from '@rocket.chat/emitter'; +import localforage from 'localforage'; +import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; import { Mongo } from 'meteor/mongo'; -import { Accounts } from 'meteor/accounts-base'; import { ReactiveVar } from 'meteor/reactive-var'; -import { Emitter } from '@rocket.chat/emitter'; -import localforage from 'localforage'; -import Notifications from '../../../notifications/client/lib/Notifications'; +import type { MinimongoCollection } from '../../../../client/definitions/MinimongoCollection'; +import { baseURI } from '../../../../client/lib/baseURI'; import { getConfig } from '../../../../client/lib/utils/getConfig'; -import { CachedCollectionManager } from './CachedCollectionManager'; -import { withDebouncing } from '../../../../lib/utils/highOrderFunctions'; import { isTruthy } from '../../../../lib/isTruthy'; -import type { MinimongoCollection } from '../../../../client/definitions/MinimongoCollection'; +import { withDebouncing } from '../../../../lib/utils/highOrderFunctions'; +import Notifications from '../../../notifications/client/lib/Notifications'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { CachedCollectionManager } from './CachedCollectionManager'; export type EventType = Extract; @@ -34,6 +35,10 @@ const hasUnserializedUpdatedAt = (record: T): record is T & { _updatedAt: Con '_updatedAt' in record && !((record as unknown as { _updatedAt: unknown })._updatedAt instanceof Date); +localforage.config({ + name: baseURI, +}); + export class CachedCollection extends Emitter<{ changed: T; removed: T }> { private static MAX_CACHE_TIME = 60 * 60 * 24 * 30; diff --git a/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts b/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts index b6d75c7ee845..845731e2450a 100644 --- a/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts +++ b/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; +import { Emitter } from '@rocket.chat/emitter'; import { Accounts } from 'meteor/accounts-base'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { Emitter } from '@rocket.chat/emitter'; import type { CachedCollection } from './CachedCollection'; diff --git a/apps/meteor/app/ui-clean-history/client/index.ts b/apps/meteor/app/ui-clean-history/client/index.ts deleted file mode 100644 index 682594792f81..000000000000 --- a/apps/meteor/app/ui-clean-history/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './lib/tabBar'; diff --git a/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts b/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts deleted file mode 100644 index 72fcbf22f598..000000000000 --- a/apps/meteor/app/ui-clean-history/client/lib/tabBar.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useMemo, lazy } from 'react'; -import { usePermission } from '@rocket.chat/ui-contexts'; -import { isRoomFederated } from '@rocket.chat/core-typings'; - -import { addAction } from '../../../../client/views/room/lib/Toolbox'; - -const template = lazy(() => import('../../../../client/views/room/contextualBar/PruneMessages')); - -addAction('clean-history', ({ room }) => { - const hasPermission = usePermission('clean-channel-history', room._id); - const federated = isRoomFederated(room); - - return useMemo( - () => - hasPermission - ? { - groups: ['channel', 'group', 'team', 'direct_multiple', 'direct'], - id: 'clean-history', - full: true, - title: 'Prune_Messages', - icon: 'eraser', - ...(federated && { - 'data-tooltip': 'Clean_History_unavailable_for_federation', - 'disabled': true, - }), - template, - order: 250, - } - : null, - [hasPermission, federated], - ); -}); diff --git a/apps/meteor/app/ui-master/public/icons.svg b/apps/meteor/app/ui-master/public/icons.svg index 4ffaa8296f64..7eb09b8394a1 100644 --- a/apps/meteor/app/ui-master/public/icons.svg +++ b/apps/meteor/app/ui-master/public/icons.svg @@ -87,7 +87,7 @@ - + @@ -286,13 +286,13 @@ - + - + @@ -362,7 +362,7 @@ - + diff --git a/apps/meteor/app/ui-master/public/icons/discussion.svg b/apps/meteor/app/ui-master/public/icons/discussion.svg index f36cd50aa7a4..81bd3b498689 100644 --- a/apps/meteor/app/ui-master/public/icons/discussion.svg +++ b/apps/meteor/app/ui-master/public/icons/discussion.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/meteor/app/ui-master/public/icons/reply-directly.svg b/apps/meteor/app/ui-master/public/icons/reply-directly.svg index 5334f969cc9b..b75848d5e96f 100644 --- a/apps/meteor/app/ui-master/public/icons/reply-directly.svg +++ b/apps/meteor/app/ui-master/public/icons/reply-directly.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/meteor/app/ui-master/public/icons/report.svg b/apps/meteor/app/ui-master/public/icons/report.svg index a0d1db38d62b..69ef87baa9ed 100644 --- a/apps/meteor/app/ui-master/public/icons/report.svg +++ b/apps/meteor/app/ui-master/public/icons/report.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/meteor/app/ui-master/public/icons/thread.svg b/apps/meteor/app/ui-master/public/icons/thread.svg index 7074dcf5cbad..9dbca758a678 100644 --- a/apps/meteor/app/ui-master/public/icons/thread.svg +++ b/apps/meteor/app/ui-master/public/icons/thread.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/meteor/app/ui-master/server/index.js b/apps/meteor/app/ui-master/server/index.js index 753a4e32396d..f9e335451d74 100644 --- a/apps/meteor/app/ui-master/server/index.js +++ b/apps/meteor/app/ui-master/server/index.js @@ -1,12 +1,12 @@ +import { Settings } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; import { Meteor } from 'meteor/meteor'; import { Inject } from 'meteor/meteorhacks:inject-initial'; import { Tracker } from 'meteor/tracker'; -import { Settings } from '@rocket.chat/models'; -import { escapeHTML } from '@rocket.chat/string-helpers'; +import { withDebouncing } from '../../../lib/utils/highOrderFunctions'; import { settings } from '../../settings/server'; import { applyHeadInjections, headInjections, injectIntoBody, injectIntoHead } from './inject'; -import { withDebouncing } from '../../../lib/utils/highOrderFunctions'; import './scripts'; diff --git a/apps/meteor/app/ui-master/server/inject.ts b/apps/meteor/app/ui-master/server/inject.ts index 0347c5dab0fb..78112bcee343 100644 --- a/apps/meteor/app/ui-master/server/inject.ts +++ b/apps/meteor/app/ui-master/server/inject.ts @@ -1,12 +1,12 @@ import crypto from 'crypto'; -import parseRequest from 'parseurl'; import type { NextHandleFunction } from 'connect'; -import { WebApp } from 'meteor/webapp'; -import { ReactiveDict } from 'meteor/reactive-dict'; import { Inject } from 'meteor/meteorhacks:inject-initial'; +import { ReactiveDict } from 'meteor/reactive-dict'; +import { WebApp } from 'meteor/webapp'; +import parseRequest from 'parseurl'; -import { getURL } from '../../utils/server'; +import { getURL } from '../../utils/server/getURL'; type Injection = | string diff --git a/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts b/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts deleted file mode 100644 index b737c112ec93..000000000000 --- a/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui'; - -import * as TabBar from './actionButtons/tabbar'; -import * as MessageAction from './actionButtons/messageAction'; -import * as MessageBox from './actionButtons/messageBox'; -import * as DropdownAction from './actionButtons/dropdownAction'; -import { sdk } from '../../utils/client/lib/SDKClient'; - -let registeredButtons: Array = []; - -const addButton = async (button: IUIActionButton): Promise => { - switch (button.context) { - case UIActionButtonContext.MESSAGE_ACTION: - MessageAction.onAdded(button); - break; - case UIActionButtonContext.ROOM_ACTION: - TabBar.onAdded(button); - break; - case UIActionButtonContext.MESSAGE_BOX_ACTION: - MessageBox.onAdded(button); - break; - case UIActionButtonContext.USER_DROPDOWN_ACTION: - await DropdownAction.onAdded(button); - break; - } - - registeredButtons.push(Object.freeze(button)); -}; - -const removeButton = async (button: IUIActionButton): Promise => { - switch (button.context) { - case UIActionButtonContext.MESSAGE_ACTION: - MessageAction.onRemoved(button); - break; - case UIActionButtonContext.ROOM_ACTION: - TabBar.onRemoved(button); - break; - case UIActionButtonContext.MESSAGE_BOX_ACTION: - MessageBox.onRemoved(button); - break; - case UIActionButtonContext.USER_DROPDOWN_ACTION: - await DropdownAction.onRemoved(button); - break; - } -}; - -export const loadButtons = (): Promise => - sdk.rest.get('/apps/actionButtons').then((value) => { - registeredButtons.forEach((button) => removeButton(button)); - registeredButtons = []; - value.map(addButton); - }); - -Meteor.startup(() => loadButtons()); diff --git a/apps/meteor/app/ui-message/client/ActionManager.js b/apps/meteor/app/ui-message/client/ActionManager.js index 48165b99f436..ebe9d1aed093 100644 --- a/apps/meteor/app/ui-message/client/ActionManager.js +++ b/apps/meteor/app/ui-message/client/ActionManager.js @@ -1,20 +1,19 @@ import { UIKitIncomingInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; -import { Meteor } from 'meteor/meteor'; -import { Random } from '@rocket.chat/random'; -import { Emitter } from '@rocket.chat/emitter'; import { UIKitInteractionTypes } from '@rocket.chat/core-typings'; +import { Emitter } from '@rocket.chat/emitter'; +import { Random } from '@rocket.chat/random'; +import { lazy } from 'react'; -import Notifications from '../../notifications/client/lib/Notifications'; -import { CachedCollectionManager } from '../../ui-cached-collection/client'; -import { t } from '../../utils/client'; import * as banners from '../../../client/lib/banners'; -import { dispatchToastMessage } from '../../../client/lib/toast'; import { imperativeModal } from '../../../client/lib/imperativeModal'; -import UiKitModal from '../../../client/views/modal/uikit/UiKitModal'; -import { sdk } from '../../utils/client/lib/SDKClient'; +import { dispatchToastMessage } from '../../../client/lib/toast'; import { router } from '../../../client/providers/RouterProvider'; +import { sdk } from '../../utils/client/lib/SDKClient'; +import { t } from '../../utils/lib/i18n'; + +const UiKitModal = lazy(() => import('../../../client/views/modal/uikit/UiKitModal')); -const events = new Emitter(); +export const events = new Emitter(); export const on = (...args) => { events.on(...args); @@ -45,7 +44,7 @@ export const generateTriggerId = (appId) => { return triggerId; }; -const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) => { +export const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) => { if (!triggersId.has(triggerId)) { return; } @@ -169,6 +168,8 @@ const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) export const triggerAction = async ({ type, actionId, appId, rid, mid, viewId, container, tmid, ...rest }) => new Promise(async (resolve, reject) => { + events.emit('busy', { busy: true }); + const triggerId = generateTriggerId(appId); const payload = rest.payload || rest; @@ -191,6 +192,8 @@ export const triggerAction = async ({ type, actionId, appId, rid, mid, viewId, c } catch (e) { reject(e); return {}; + } finally { + events.emit('busy', { busy: false }); } })(); @@ -256,11 +259,3 @@ export const getUserInteractionPayloadByViewId = (viewId) => { return instance.payload; }; - -Meteor.startup(() => - CachedCollectionManager.onLogin(() => - Notifications.onUser('uiInteraction', ({ type, ...data }) => { - handlePayloadUserInteraction(type, data); - }), - ), -); diff --git a/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts b/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts deleted file mode 100644 index 07ce832e59f2..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { AccountBox } from '../../../ui-utils/client/lib/AccountBox'; - -export const onAdded = async (button: IUIActionButton): Promise => { - const { appId, actionId, labelI18n, context } = button; - await AccountBox.addItem({ - ...button, - name: button.labelI18n, - appId, - actionId, - labelI18n, - context, - isAppButtonItem: true, - }); -}; -export const onRemoved = async (button: IUIActionButton): Promise => { - const { appId, actionId, labelI18n, context } = button; - await AccountBox.deleteItem({ - ...button, - name: button.labelI18n, - appId, - actionId, - labelI18n, - context, - isAppButtonItem: true, - }); -}; diff --git a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts b/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts deleted file mode 100644 index be38eaa6a22c..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* Style disabled as having some arrow functions in one-line hurts readability */ -/* eslint-disable arrow-body-style */ - -import { Meteor } from 'meteor/meteor'; -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui'; -import type { IRoom } from '@rocket.chat/core-typings'; -import { - isDirectMessageRoom, - isMultipleDirectMessageRoom, - isOmnichannelRoom, - isPrivateDiscussion, - isPrivateTeamRoom, - isPublicDiscussion, - isPublicTeamRoom, -} from '@rocket.chat/core-typings'; - -import { hasAtLeastOnePermission, hasPermission, hasRole, hasAnyRole } from '../../../../authorization/client'; - -const applyAuthFilter = (button: IUIActionButton, room?: IRoom, ignoreSubscriptions = false): boolean => { - const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {}; - - const userId = Meteor.userId(); - - const hasAllPermissionsResult = hasAllPermissions ? hasPermission(hasAllPermissions) : true; - const hasOnePermissionResult = hasOnePermission ? hasAtLeastOnePermission(hasOnePermission) : true; - const hasAllRolesResult = hasAllRoles - ? !!userId && hasAllRoles.every((role) => hasRole(userId, role, room?._id, ignoreSubscriptions)) - : true; - const hasOneRoleResult = hasOneRole ? !!userId && hasAnyRole(userId, hasOneRole, room?._id, ignoreSubscriptions) : true; - - return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult; -}; - -const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = { - [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c', - [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p', - [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom, - [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom, - [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion, - [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion, - [RoomTypeFilter.DIRECT]: isDirectMessageRoom, - [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom, - [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom, -}; - -const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => { - const { roomTypes } = button.when || {}; - return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room)); -}; - -export const applyButtonFilters = (button: IUIActionButton, room?: IRoom): boolean => { - return applyAuthFilter(button, room) && (!room || applyRoomFilter(button, room)); -}; - -export const applyDropdownActionButtonFilters = (button: IUIActionButton): boolean => { - return applyAuthFilter(button, undefined, true); -}; diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts deleted file mode 100644 index 29a45ded6a1d..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { MessageAction } from '../../../ui-utils/client'; -import { messageArgs } from '../../../../client/lib/utils/messageArgs'; -import { triggerActionButtonAction } from '../ActionManager'; -import { applyButtonFilters } from './lib/applyButtonFilters'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -// eslint-disable-next-line no-void -export const onAdded = (button: IUIActionButton): void => - MessageAction.addButton({ - id: getIdForActionButton(button), - icon: '' as any, - label: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), - context: button.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred'], - condition({ room }) { - return applyButtonFilters(button, room); - }, - action(_, props) { - const { message = messageArgs(this).msg } = props; - void triggerActionButtonAction({ - rid: message.rid, - mid: message._id, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context }, - }); - }, - }); - -export const onRemoved = (button: IUIActionButton): void => MessageAction.removeButton(getIdForActionButton(button)); diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts deleted file mode 100644 index d07ecbc23ad8..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { ChatRoom } from '../../../models/client'; -import { messageBox } from '../../../ui-utils/client'; -import { applyButtonFilters } from './lib/applyButtonFilters'; -import { triggerActionButtonAction } from '../ActionManager'; -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { RoomManager } from '../../../../client/lib/RoomManager'; -import { asReactiveSource } from '../../../../client/lib/tracker'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -export const onAdded = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void messageBox.actions.add('Apps', Utilities.getI18nKeyForApp(button.labelI18n, button.appId), { - id: getIdForActionButton(button), - // icon: button.icon || '', - condition() { - return applyButtonFilters( - button, - ChatRoom.findOne( - asReactiveSource( - (cb) => RoomManager.on('changed', cb), - () => RoomManager.opened, - ), - ), - ); - }, - action(params) { - void triggerActionButtonAction({ - rid: params.rid, - tmid: params.tmid, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context, message: params.chat.composer?.text }, - }); - }, - }); - -export const onRemoved = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void messageBox.actions.remove('Apps', new RegExp(getIdForActionButton(button))); diff --git a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts deleted file mode 100644 index 26352cd9b59a..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { addAction, deleteAction } from '../../../../client/views/room/lib/Toolbox'; -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { triggerActionButtonAction } from '../ActionManager'; -import { applyButtonFilters } from './lib/applyButtonFilters'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -export const onAdded = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void addAction(getIdForActionButton(button), ({ room }) => - applyButtonFilters(button, room) - ? { - id: button.actionId, - icon: undefined, // Apps won't provide icons for now - order: 300, // Make sure the button only shows up inside the room toolbox - title: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), - // Filters were applied in the applyButtonFilters function - // if the code made it this far, the button should be shown - groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], - action: (): any => - triggerActionButtonAction({ - rid: room._id, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context }, - }), - } - : null, - ); - -export const onRemoved = (button: IUIActionButton): boolean => deleteAction(getIdForActionButton(button)); diff --git a/apps/meteor/app/ui-message/client/findParentMessage.ts b/apps/meteor/app/ui-message/client/findParentMessage.ts index f5fa3888f8f4..0a3197c463a6 100644 --- a/apps/meteor/app/ui-message/client/findParentMessage.ts +++ b/apps/meteor/app/ui-message/client/findParentMessage.ts @@ -1,8 +1,8 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { callWithErrorHandling } from '../../../client/lib/utils/callWithErrorHandling'; -import { ChatMessage } from '../../models/client'; import { withDebouncing } from '../../../lib/utils/highOrderFunctions'; +import { ChatMessage } from '../../models/client'; export const findParentMessage = (() => { const waiting: string[] = []; @@ -11,7 +11,7 @@ export const findParentMessage = (() => { resolve = r; }); - const getMessages = withDebouncing({ wait: 500 })(async function () { + const getMessages = withDebouncing({ wait: 500 })(async () => { const _tmp = [...waiting]; waiting.length = 0; resolve(callWithErrorHandling('getMessages', _tmp)); diff --git a/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts b/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts index 388977e99af7..4609797e6bd2 100644 --- a/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts +++ b/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; import type { IMessage } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; +import { Meteor } from 'meteor/meteor'; -import { withDebouncing } from '../../../../lib/utils/highOrderFunctions'; import type { ComposerAPI } from '../../../../client/lib/chats/ChatAPI'; +import { withDebouncing } from '../../../../lib/utils/highOrderFunctions'; import type { FormattingButton } from './messageBoxFormatting'; import { formattingButtons } from './messageBoxFormatting'; diff --git a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts index bc3fb8a011cc..8c170f894aa4 100644 --- a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts +++ b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts @@ -1,13 +1,12 @@ -import type { Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; import { settings } from '../../../settings/client'; export type FormattingButton = | { label: TranslationKey; - icon: ComponentProps['name']; + icon: IconName; pattern: string; // text?: () => string | undefined; command?: string; diff --git a/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx b/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx index a4787bfc616a..5ead8ef295a5 100644 --- a/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx +++ b/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx @@ -1,9 +1,9 @@ import { Box, Option, OptionSkeleton, Tile } from '@rocket.chat/fuselage'; import { useUniqueId, useContentBoxSize } from '@rocket.chat/fuselage-hooks'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import type { UseQueryResult } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { useEffect, memo, useMemo, useRef } from 'react'; -import { useTranslation } from '@rocket.chat/ui-contexts'; export type ComposerBoxPopupProps< T extends { @@ -77,13 +77,13 @@ const ComposerBoxPopup = < return ( - + {title && ( - + {title} )} - + {!isLoading && itemsFlat.length === 0 && } {isLoading && } {itemsFlat.map((item, index) => { diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupCannedResponse.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupCannedResponse.tsx index ec02a67aba98..5ce1b4f11e9e 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupCannedResponse.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupCannedResponse.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; +import React from 'react'; type ComposerBoxPopupCannedResponseProps = { _id: string; diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupEmoji.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupEmoji.tsx index eb6f3f9e296c..c8586e027040 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupEmoji.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupEmoji.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; +import React from 'react'; import Emoji from '../../../../../../client/components/Emoji'; diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupRoom.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupRoom.tsx index 62ebd0841e08..368276e70b98 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupRoom.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupRoom.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; import type { IRoom } from '@rocket.chat/core-typings'; +import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; +import React from 'react'; import { RoomIcon } from '../../../../../../client/components/RoomIcon'; diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupSlashCommand.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupSlashCommand.tsx index 95bcb2709bc5..e24c44c9a1ed 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupSlashCommand.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupSlashCommand.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { OptionColumn, OptionContent, OptionDescription, OptionInput } from '@rocket.chat/fuselage'; +import React from 'react'; export type ComposerBoxPopupSlashCommandProps = { _id: string; diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupUser.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupUser.tsx index af25c4535cb0..20e20aae4238 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupUser.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopup/ComposerBoxPopupUser.tsx @@ -1,9 +1,9 @@ -import React from 'react'; -import { useTranslation } from '@rocket.chat/ui-contexts'; import { OptionAvatar, OptionColumn, OptionContent, OptionInput } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; -import UserAvatar from '../../../../../../client/components/avatar/UserAvatar'; import ReactiveUserStatus from '../../../../../../client/components/UserStatus/ReactiveUserStatus'; +import UserAvatar from '../../../../../../client/components/avatar/UserAvatar'; export type ComposerBoxPopupUserProps = { _id: string; diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx index 45188e62122c..0a2478f7bf9a 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx @@ -19,7 +19,7 @@ const ComposerBoxPopupPreview = forwardRef< tmid?: string; suspended?: boolean; } ->(({ focused, items, rid, tmid, select, suspended }, ref) => { +>(function ComposerBoxPopupPreview({ focused, items, rid, tmid, select, suspended }, ref) { const id = useUniqueId(); const chat = useChat(); const executeSlashCommandPreviewMethod = useMethod('executeSlashCommandPreview'); @@ -92,12 +92,12 @@ const ComposerBoxPopupPreview = forwardRef< return ( - + {isLoading && Array(5) .fill(5) - .map((_, index) => )} + .map((_, index) => )} {!isLoading && itemsFlat.map((item) => ( @@ -111,7 +111,7 @@ const ComposerBoxPopupPreview = forwardRef< borderColor={item === focused ? 'highlight' : 'transparent'} tabIndex={item === focused ? 0 : -1} aria-selected={item === focused} - m='x2' + m={2} borderWidth='default' borderRadius='x4' > diff --git a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts index 7d65d5e8088a..dceac6887191 100644 --- a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts +++ b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts @@ -3,7 +3,7 @@ import { useQueries } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; import type { ComposerPopupOption } from '../../../../../client/views/room/contexts/ComposerPopupContext'; -import { slashCommands } from '../../../../utils/client'; +import { slashCommands } from '../../../../utils/lib/slashCommand'; import { useEnablePopupPreview } from './useEnablePopupPreview'; export const useComposerBoxPopupQueries = (filter: unknown, popup?: ComposerPopupOption) => { diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupConfig.ts b/apps/meteor/app/ui-message/client/popup/messagePopupConfig.ts index 654b627121ed..1d220f8bce8d 100644 --- a/apps/meteor/app/ui-message/client/popup/messagePopupConfig.ts +++ b/apps/meteor/app/ui-message/client/popup/messagePopupConfig.ts @@ -1,11 +1,11 @@ +import type { IUser } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Mongo } from 'meteor/mongo'; import { Tracker } from 'meteor/tracker'; -import type { IUser } from '@rocket.chat/core-typings'; -import { Messages } from '../../../models/client'; -import { asReactiveSource } from '../../../../client/lib/tracker'; import { RoomManager } from '../../../../client/lib/RoomManager'; +import { asReactiveSource } from '../../../../client/lib/tracker'; +import { Messages } from '../../../models/client'; export const usersFromRoomMessages = new Mongo.Collection<{ _id: string; @@ -41,6 +41,7 @@ Meteor.startup(() => { fields: { 'u.username': 1, 'u.name': 1, + 'u._id': 1, 'ts': 1, }, sort: { ts: -1 }, @@ -52,9 +53,9 @@ Meteor.startup(() => { uniqueMessageUsersControl[username] = true; return notMapped; }) - .forEach(({ u: { username, name }, ts }) => - usersFromRoomMessages.upsert(username, { - _id: username, + .forEach(({ u: { username, name, _id }, ts }) => + usersFromRoomMessages.upsert(_id, { + _id, username, name, ts, diff --git a/apps/meteor/app/ui-utils/client/index.ts b/apps/meteor/app/ui-utils/client/index.ts index 83a1057a7287..6409db5a3592 100644 --- a/apps/meteor/app/ui-utils/client/index.ts +++ b/apps/meteor/app/ui-utils/client/index.ts @@ -1,9 +1,7 @@ import './lib/messageActionDefault'; -export { AccountBox } from './lib/AccountBox'; export { MessageAction } from './lib/MessageAction'; export { messageBox } from './lib/messageBox'; -export { readMessage } from './lib/readMessages'; export { LegacyRoomManager } from './lib/LegacyRoomManager'; export { upsertMessage, RoomHistoryManager } from './lib/RoomHistoryManager'; export { mainReady } from './lib/mainReady'; diff --git a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts index 3ed3a60b3bf6..f649b6761e76 100644 --- a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts @@ -1,12 +1,8 @@ import type { IUIActionButton, IUActionButtonWhen } from '@rocket.chat/apps-engine/definition/ui/IUIActionButtonDescriptor'; import type { UserStatus } from '@rocket.chat/core-typings'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey, LocationPathname } from '@rocket.chat/ui-contexts'; -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; -import { applyDropdownActionButtonFilters } from '../../../ui-message/client/actionButtons/lib/applyButtonFilters'; import { sdk } from '../../../utils/client/lib/SDKClient'; export interface IAppAccountBoxItem extends IUIActionButton { @@ -21,7 +17,7 @@ export interface IAppAccountBoxItem extends IUIActionButton { export type AccountBoxItem = { name: TranslationKey; - icon: ComponentProps['name']; + icon: IconName; href: LocationPathname; sideNav?: string; condition: () => boolean; @@ -30,38 +26,9 @@ export type AccountBoxItem = { export const isAppAccountBoxItem = (item: IAppAccountBoxItem | AccountBoxItem): item is IAppAccountBoxItem => 'isAppButtonItem' in item; class AccountBoxBase { - private items = new ReactiveVar([]); - public setStatus(status: UserStatus, statusText?: string): any { return sdk.rest.post('/v1/users.setStatus', { status, message: statusText }); } - - public async addItem(newItem: IAppAccountBoxItem): Promise { - Tracker.nonreactive(() => { - const actual = this.items.get(); - actual.push(newItem); - this.items.set(actual); - }); - } - - public async deleteItem(item: IAppAccountBoxItem): Promise { - Tracker.nonreactive(() => { - const actual = this.items.get(); - const itemIndex = actual.findIndex((actualItem: IAppAccountBoxItem) => actualItem.appId === item.appId); - actual.splice(itemIndex, 1); - this.items.set(actual); - }); - } - - public getItems(): (IAppAccountBoxItem | AccountBoxItem)[] { - return this.items.get().filter((item: IAppAccountBoxItem | AccountBoxItem) => { - if ('condition' in item) { - return item.condition(); - } - - return applyDropdownActionButtonFilters(item); - }); - } } export const AccountBox = new AccountBoxBase(); diff --git a/apps/meteor/app/ui-utils/client/lib/IframeLogin.ts b/apps/meteor/app/ui-utils/client/lib/IframeLogin.ts index 1132b86cda8f..160ebba96747 100644 --- a/apps/meteor/app/ui-utils/client/lib/IframeLogin.ts +++ b/apps/meteor/app/ui-utils/client/lib/IframeLogin.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; import { Accounts } from 'meteor/accounts-base'; +import { Match } from 'meteor/check'; +import { HTTP } from 'meteor/http'; +import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; -import { HTTP } from 'meteor/http'; import { settings } from '../../../settings/client'; diff --git a/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts b/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts index f85fecd74ac1..85aafa4bb185 100644 --- a/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts +++ b/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts @@ -1,19 +1,19 @@ -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import type { Mongo } from 'meteor/mongo'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { RoomManager } from '../../../../client/lib/RoomManager'; +import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent'; -import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; -import { mainReady } from './mainReady'; +import { getConfig } from '../../../../client/lib/utils/getConfig'; +import { router } from '../../../../client/providers/RouterProvider'; import { callbacks } from '../../../../lib/callbacks'; import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription, ChatRoom } from '../../../models/client'; -import { getConfig } from '../../../../client/lib/utils/getConfig'; -import { RoomManager } from '../../../../client/lib/RoomManager'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; import { Notifications } from '../../../notifications/client'; import { sdk } from '../../../utils/client/lib/SDKClient'; -import { router } from '../../../../client/providers/RouterProvider'; +import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; +import { mainReady } from './mainReady'; const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen') ?? '5') || 5; diff --git a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts index 47fdbb311765..043dfe87b606 100644 --- a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts +++ b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts @@ -1,29 +1,15 @@ -import type { ComponentProps, ContextType } from 'react'; -import mem from 'mem'; -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import type { Icon } from '@rocket.chat/fuselage'; -import type { IMessage, IUser, ISubscription, IRoom, SettingValue, Serialized, ITranslatedMessage } from '@rocket.chat/core-typings'; +import type { IMessage, IUser, ISubscription, IRoom, SettingValue, ITranslatedMessage } from '@rocket.chat/core-typings'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import mem from 'mem'; +import type { ContextType } from 'react'; -import { Messages, ChatRoom, Subscriptions } from '../../../models/client'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; -import type { ToolboxContextValue } from '../../../../client/views/room/contexts/ToolboxContext'; -import type { ChatContext } from '../../../../client/views/room/contexts/ChatContext'; import type { AutoTranslateOptions } from '../../../../client/views/room/MessageList/hooks/useAutoTranslate'; -import { sdk } from '../../../utils/client/lib/SDKClient'; - -const getMessage = async (msgId: string): Promise | null> => { - try { - const { message } = await sdk.rest.get('/v1/chat.getMessage', { msgId }); - return message; - } catch { - return null; - } -}; +import type { ChatContext } from '../../../../client/views/room/contexts/ChatContext'; +import type { RoomToolboxContextValue } from '../../../../client/views/room/contexts/RoomToolboxContext'; type MessageActionGroup = 'message' | 'menu'; + export type MessageActionContext = | 'message' | 'threads' @@ -34,7 +20,10 @@ export type MessageActionContext = | 'mentions' | 'federated' | 'videoconf' - | 'search'; + | 'search' + | 'videoconf-threads'; + +type MessageActionType = 'communication' | 'interaction' | 'duplication' | 'apps' | 'management'; type MessageActionConditionProps = { message: IMessage; @@ -48,7 +37,7 @@ type MessageActionConditionProps = { export type MessageActionConfig = { id: string; - icon: ComponentProps['name']; + icon: IconName; variant?: 'danger' | 'success' | 'warning'; label: TranslationKey; order?: number; @@ -58,7 +47,8 @@ export type MessageActionConfig = { group?: MessageActionGroup | MessageActionGroup[]; context?: MessageActionContext[]; action: ( - e: Pick, + this: any, + e: Pick, { message, tabbar, @@ -66,33 +56,21 @@ export type MessageActionConfig = { chat, autoTranslateOptions, }: { - message?: IMessage & Partial; - tabbar: ToolboxContextValue; + message: IMessage & Partial; + tabbar: RoomToolboxContextValue; room?: IRoom; chat: ContextType; autoTranslateOptions?: AutoTranslateOptions; }, ) => any; condition?: (props: MessageActionConditionProps) => Promise | boolean; + type?: MessageActionType; }; -type MessageActionConfigList = MessageActionConfig[]; - -export const MessageAction = new (class { - /* - config expects the following keys (only id is mandatory): - id (mandatory) - icon: string - label: string - action: function(event, instance) - condition: function(message) - order: integer - group: string (message or menu) - */ +class MessageAction { + public buttons: Record = {}; - buttons = new ReactiveVar>({}); - - addButton(config: MessageActionConfig): void { + public addButton(config: MessageActionConfig): void { if (!config?.id) { return; } @@ -105,106 +83,34 @@ export const MessageAction = new (class { config.condition = mem(config.condition, { maxAge: 1000, cacheKey: JSON.stringify }); } - return Tracker.nonreactive(() => { - const btns = this.buttons.get(); - btns[config.id] = config; - mem.clear(this._getButtons); - mem.clear(this.getButtonsByGroup); - return this.buttons.set(btns); - }); - } - - removeButton(id: MessageActionConfig['id']): void { - return Tracker.nonreactive(() => { - const btns = this.buttons.get(); - delete btns[id]; - return this.buttons.set(btns); - }); - } - - updateButton(id: MessageActionConfig['id'], config: MessageActionConfig): void { - return Tracker.nonreactive(() => { - const btns = this.buttons.get(); - if (btns[id]) { - btns[id] = Object.assign(btns[id], config); - return this.buttons.set(btns); - } - }); + this.buttons[config.id] = config; } - getButtonById(id: MessageActionConfig['id']): MessageActionConfig | undefined { - const allButtons = this.buttons.get(); - return allButtons[id]; + public removeButton(id: MessageActionConfig['id']): void { + delete this.buttons[id]; } - _getButtons = mem((): MessageActionConfigList => Object.values(this.buttons.get()).sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), { - maxAge: 1000, - }); - - async getButtonsByCondition( - prop: MessageActionConditionProps, - arr: MessageActionConfigList = MessageAction._getButtons(), - ): Promise { + public async getAll( + props: MessageActionConditionProps, + context: MessageActionContext, + group: MessageActionGroup, + ): Promise { return ( await Promise.all( - arr.map(async (button) => { - return [button, !button.condition || (await button.condition(prop))] as const; - }), + Object.values(this.buttons) + .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) + .filter((button) => !button.group || (Array.isArray(button.group) ? button.group.includes(group) : button.group === group)) + .filter((button) => !button.context || button.context.includes(context)) + .map(async (button) => { + return [button, !button.condition || (await button.condition({ ...props, context }))] as const; + }), ) ) .filter(([, condition]) => condition) .map(([button]) => button); } +} - getButtonsByGroup = mem( - function (group: MessageActionGroup, arr: MessageActionConfigList = MessageAction._getButtons()): MessageActionConfigList { - return arr.filter((button) => !button.group || (Array.isArray(button.group) ? button.group.includes(group) : button.group === group)); - }, - { maxAge: 1000 }, - ); - - getButtonsByContext(context: MessageActionContext, arr: MessageActionConfigList): MessageActionConfigList { - return !context ? arr : arr.filter((button) => !button.context || button.context.includes(context)); - } - - async getButtons( - props: MessageActionConditionProps, - context: MessageActionContext, - group: MessageActionGroup, - ): Promise { - const allButtons = group ? this.getButtonsByGroup(group) : MessageAction._getButtons(); - - if (props.message) { - return this.getButtonsByCondition({ ...props, context }, this.getButtonsByContext(context, allButtons)); - } - return allButtons; - } +const instance = new MessageAction(); - resetButtons(): void { - mem.clear(this._getButtons); - mem.clear(this.getButtonsByGroup); - return this.buttons.set({}); - } - - async getPermaLink(msgId: string): Promise { - if (!msgId) { - throw new Error('invalid-parameter'); - } - - const msg = Messages.findOne(msgId) || (await getMessage(msgId)); - if (!msg) { - throw new Error('message-not-found'); - } - const roomData = ChatRoom.findOne({ - _id: msg.rid, - }); - - if (!roomData) { - throw new Error('room-not-found'); - } - - const subData = Subscriptions.findOne({ 'rid': roomData._id, 'u._id': Meteor.userId() }); - const roomURL = roomCoordinator.getURL(roomData.t, { ...(subData || roomData), tab: '' }); - return `${roomURL}?msg=${msgId}`; - } -})(); +export { instance as MessageAction }; diff --git a/apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts b/apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts index 879977f7052d..4e2f0c020a12 100644 --- a/apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts +++ b/apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts @@ -1,19 +1,18 @@ -import { Tracker } from 'meteor/tracker'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { v4 as uuidv4 } from 'uuid'; -import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'; -import { Emitter } from '@rocket.chat/emitter'; import type { IMessage, IRoom, ISubscription } from '@rocket.chat/core-typings'; +import { Emitter } from '@rocket.chat/emitter'; +import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; import type { MutableRefObject } from 'react'; +import { v4 as uuidv4 } from 'uuid'; -import { waitForElement } from '../../../../client/lib/utils/waitForElement'; -import { readMessage } from './readMessages'; -import { getUserPreference } from '../../../utils/client'; +import type { MinimongoCollection } from '../../../../client/definitions/MinimongoCollection'; +import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived'; +import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; import { getConfig } from '../../../../client/lib/utils/getConfig'; +import { waitForElement } from '../../../../client/lib/utils/waitForElement'; import { ChatMessage, ChatSubscription } from '../../../models/client'; -import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; -import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived'; -import type { MinimongoCollection } from '../../../../client/definitions/MinimongoCollection'; +import { getUserPreference } from '../../../utils/client'; export async function upsertMessage( { @@ -196,14 +195,12 @@ class RoomHistoryManagerClass extends Emitter { } waitAfterFlush(() => { + this.emit('loaded-messages'); const heightDiff = wrapper.scrollHeight - (previousHeight ?? NaN); wrapper.scrollTop = (scroll ?? NaN) + heightDiff; }); room.isLoading.set(false); - waitAfterFlush(() => { - readMessage.refreshUnreadMark(rid); - }); } public async getMoreNext(rid: IRoom['_id'], atBottomRef: MutableRefObject) { @@ -299,9 +296,8 @@ class RoomHistoryManagerClass extends Emitter { upsertMessageBulk({ msgs: Array.from(result.messages).filter((msg) => msg.t !== 'command'), subscription }); - readMessage.refreshUnreadMark(message.rid); - Tracker.afterFlush(async () => { + this.emit('loaded-messages'); room.isLoading.set(false); }); diff --git a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts index d00e6bf9675d..5807673188e5 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts @@ -1,20 +1,21 @@ -import moment from 'moment'; -import { Meteor } from 'meteor/meteor'; import type { IMessage } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; +import moment from 'moment'; -import ShareMessageModal from '../../../../client/views/room/modals/ShareMessageModal'; -import { messageArgs } from '../../../../client/lib/utils/messageArgs'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; -import { ChatRoom, Subscriptions } from '../../../models/client'; -import { hasAtLeastOnePermission, hasPermission } from '../../../authorization/client'; -import { MessageAction } from './MessageAction'; +import { getPermaLink } from '../../../../client/lib/getPermaLink'; import { imperativeModal } from '../../../../client/lib/imperativeModal'; +import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; +import { dispatchToastMessage } from '../../../../client/lib/toast'; +import { messageArgs } from '../../../../client/lib/utils/messageArgs'; +import { router } from '../../../../client/providers/RouterProvider'; +import ForwardMessageModal from '../../../../client/views/room/modals/ForwardMessageModal/ForwardMessageModal'; import ReactionList from '../../../../client/views/room/modals/ReactionListModal'; import ReportMessageModal from '../../../../client/views/room/modals/ReportMessageModal'; -import { dispatchToastMessage } from '../../../../client/lib/toast'; +import { hasAtLeastOnePermission, hasPermission } from '../../../authorization/client'; +import { ChatRoom, Subscriptions } from '../../../models/client'; import { t } from '../../../utils/lib/i18n'; -import { router } from '../../../../client/providers/RouterProvider'; +import { MessageAction } from './MessageAction'; const getMainMessageText = (message: IMessage): IMessage => { const newMessage = { ...message }; @@ -23,13 +24,14 @@ const getMainMessageText = (message: IMessage): IMessage => { return { ...newMessage }; }; -Meteor.startup(async function () { +Meteor.startup(async () => { MessageAction.addButton({ id: 'reply-directly', icon: 'reply-directly', label: 'Reply_in_direct_message', context: ['message', 'message-mobile', 'threads', 'federated'], role: 'link', + type: 'communication', action(_, props) { const { message = messageArgs(this).msg } = props; roomCoordinator.openRouteLink( @@ -64,15 +66,16 @@ Meteor.startup(async function () { }); MessageAction.addButton({ - id: 'share-message', + id: 'forward-message', icon: 'arrow-forward', - label: 'Share_Message', + label: 'Forward_message', context: ['message', 'message-mobile', 'threads'], + type: 'communication', async action(_, props) { const { message = messageArgs(this).msg } = props; - const permalink = await MessageAction.getPermaLink(message._id); + const permalink = await getPermaLink(message._id); imperativeModal.open({ - component: ShareMessageModal, + component: ForwardMessageModal, props: { message, permalink, @@ -83,7 +86,7 @@ Meteor.startup(async function () { }); }, order: 0, - group: ['message', 'menu'], + group: 'message', }); MessageAction.addButton({ @@ -111,19 +114,20 @@ Meteor.startup(async function () { return true; }, order: -2, - group: ['message', 'menu'], + group: 'message', }); MessageAction.addButton({ id: 'permalink', icon: 'permalink', - label: 'Get_link', + label: 'Copy_link', // classes: 'clipboard', - context: ['message', 'message-mobile', 'threads', 'federated'], + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], + type: 'duplication', async action(_, props) { try { const { message = messageArgs(this).msg } = props; - const permalink = await MessageAction.getPermaLink(message._id); + const permalink = await getPermaLink(message._id); await navigator.clipboard.writeText(permalink); dispatchToastMessage({ type: 'success', message: t('Copied') }); } catch (e) { @@ -133,16 +137,17 @@ Meteor.startup(async function () { condition({ subscription }) { return !!subscription; }, - order: 4, + order: 5, group: 'menu', }); MessageAction.addButton({ id: 'copy', icon: 'copy', - label: 'Copy', + label: 'Copy_text', // classes: 'clipboard', context: ['message', 'message-mobile', 'threads', 'federated'], + type: 'duplication', async action(_, props) { const { message = messageArgs(this).msg } = props; const msgText = getMainMessageText(message).msg; @@ -152,7 +157,7 @@ Meteor.startup(async function () { condition({ subscription }) { return !!subscription; }, - order: 5, + order: 6, group: 'menu', }); @@ -161,20 +166,21 @@ Meteor.startup(async function () { icon: 'edit', label: 'Edit', context: ['message', 'message-mobile', 'threads', 'federated'], + type: 'management', async action(_, props) { const { message = messageArgs(this).msg, chat } = props; await chat?.messageEditing.editMessage(message); }, - condition({ message, subscription, settings, room }) { + condition({ message, subscription, settings, room, user }) { if (subscription == null) { return false; } if (isRoomFederated(room)) { - return message.u._id === Meteor.userId(); + return message.u._id === user?._id; } const canEditMessage = hasAtLeastOnePermission('edit-message', message.rid); const isEditAllowed = settings.Message_AllowEditing; - const editOwn = message.u && message.u._id === Meteor.userId(); + const editOwn = message.u && message.u._id === user?._id; if (!(canEditMessage || (isEditAllowed && editOwn))) { return false; } @@ -194,7 +200,7 @@ Meteor.startup(async function () { } return true; }, - order: 6, + order: 8, group: 'menu', }); @@ -202,17 +208,18 @@ Meteor.startup(async function () { id: 'delete-message', icon: 'trash', label: 'Delete', - context: ['message', 'message-mobile', 'threads', 'federated'], + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], color: 'alert', + type: 'management', async action(this: unknown, _, { message = messageArgs(this).msg, chat }) { await chat?.flows.requestMessageDeletion(message); }, - condition({ message, subscription, room, chat }) { + condition({ message, subscription, room, chat, user }) { if (!subscription) { return false; } if (isRoomFederated(room)) { - return message.u._id === Meteor.userId(); + return message.u._id === user?._id; } const isLivechatRoom = roomCoordinator.isLivechatRoom(room.t); if (isLivechatRoom) { @@ -221,7 +228,7 @@ Meteor.startup(async function () { return chat?.data.canDeleteMessage(message) ?? false; }, - order: 18, + order: 10, group: 'menu', }); @@ -229,8 +236,9 @@ Meteor.startup(async function () { id: 'report-message', icon: 'report', label: 'Report', - context: ['message', 'message-mobile', 'threads', 'federated'], + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], color: 'alert', + type: 'management', action(this: unknown, _, { message = messageArgs(this).msg }) { imperativeModal.open({ component: ReportMessageModal, @@ -240,14 +248,15 @@ Meteor.startup(async function () { }, }); }, - condition({ subscription, room }) { + condition({ subscription, room, message, user }) { const isLivechatRoom = roomCoordinator.isLivechatRoom(room.t); - if (isLivechatRoom) { + if (isLivechatRoom || message.u._id === user?._id) { return false; } + return Boolean(subscription); }, - order: 17, + order: 9, group: 'menu', }); @@ -255,7 +264,8 @@ Meteor.startup(async function () { id: 'reaction-list', icon: 'emoji', label: 'Reactions', - context: ['message', 'message-mobile', 'threads'], + context: ['message', 'message-mobile', 'threads', 'videoconf', 'videoconf-threads'], + type: 'interaction', action(this: unknown, _, { message: { reactions = {} } = messageArgs(this).msg }) { imperativeModal.open({ component: ReactionList, @@ -265,7 +275,7 @@ Meteor.startup(async function () { condition({ message: { reactions } }) { return !!reactions; }, - order: 18, + order: 9, group: 'menu', }); }); diff --git a/apps/meteor/app/ui-utils/client/lib/messageBox.ts b/apps/meteor/app/ui-utils/client/lib/messageBox.ts index e5cd105741de..3f3c545af57e 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageBox.ts @@ -3,7 +3,7 @@ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ChatAPI } from '../../../../client/lib/chats/ChatAPI'; -type MessageBoxAction = { +export type MessageBoxAction = { label: TranslationKey; id: string; icon?: string; diff --git a/apps/meteor/app/ui-utils/client/lib/readMessages.ts b/apps/meteor/app/ui-utils/client/lib/readMessages.ts deleted file mode 100644 index 6a09a503552f..000000000000 --- a/apps/meteor/app/ui-utils/client/lib/readMessages.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Emitter } from '@rocket.chat/emitter'; -import type { IRoom } from '@rocket.chat/core-typings'; - -import { RoomHistoryManager } from './RoomHistoryManager'; -import { LegacyRoomManager } from './LegacyRoomManager'; -import { ChatSubscription, ChatMessage } from '../../../models/client'; -import { RoomManager } from '../../../../client/lib/RoomManager'; -import { sdk } from '../../../utils/client/lib/SDKClient'; - -class ReadMessage extends Emitter { - protected enabled: boolean; - - protected debug = false; - - constructor() { - super(); - this.enable(); - } - - protected log(...args: any[]) { - return this.debug && console.log(...args); - } - - public enable() { - this.enabled = document.hasFocus(); - } - - public disable() { - this.enabled = false; - } - - public isEnable() { - return this.enabled === true; - } - - public read(rid: IRoom['_id'] | undefined = RoomManager.opened) { - if (!this.enabled) { - this.log('readMessage -> readNow canceled by enabled: false'); - return; - } - - if (!rid) { - this.log('readMessage -> readNow canceled by rid: undefined'); - return; - } - - const subscription = ChatSubscription.findOne({ rid }); - if (!subscription) { - this.log('readMessage -> readNow canceled, no subscription found for rid:', rid); - return; - } - - if (subscription.alert === false && subscription.unread === 0) { - this.log('readMessage -> readNow canceled, alert', subscription.alert, 'and unread', subscription.unread); - return; - } - - const room = LegacyRoomManager.getOpenedRoomByRid(rid); - if (!room) { - this.log('readMessage -> readNow canceled, no room found for typeName:', subscription.t + subscription.name); - return; - } - - // Only read messages if user saw the first unread message - const unreadMark = document.querySelector('.message.first-unread, .rcx-message-divider--unread'); - if (unreadMark) { - const visible = unreadMark.offsetTop >= 0; - - if (!visible) { - this.log('readMessage -> readNow canceled, unread mark visible:', visible); - return; - } - // if unread mark is not visible and there is more more not loaded unread messages - } else if (RoomHistoryManager.getRoom(rid).unreadNotLoaded.get() > 0) { - return; - } - - return this.readNow(rid); - } - - public readNow(rid: IRoom['_id'] | undefined = RoomManager.opened) { - if (!rid) { - this.log('readMessage -> readNow canceled, no rid informed'); - return; - } - - const subscription = ChatSubscription.findOne({ rid }); - if (!subscription) { - this.log('readMessage -> readNow canceled, no subscription found for rid:', rid); - return; - } - - return sdk.rest.post('/v1/subscriptions.read', { rid }).then(() => { - RoomHistoryManager.getRoom(rid).unreadNotLoaded.set(0); - return this.emit(rid); - }); - } - - public refreshUnreadMark(rid: IRoom['_id']) { - if (!rid) { - return; - } - - const subscription = ChatSubscription.findOne({ rid }, { reactive: false }); - if (!subscription) { - return; - } - - const room = LegacyRoomManager.getOpenedRoomByRid(rid); - if (!room) { - return; - } - - if (!subscription.alert && subscription.unread === 0) { - document.querySelector('.message.first-unread')?.classList.remove('first-unread'); - room.unreadSince.set(undefined); - return; - } - - let lastReadRecord = ChatMessage.findOne( - { - rid: subscription.rid, - ts: { - $lt: subscription.ls, - }, - }, - { - sort: { - ts: -1, - }, - }, - ) as { ts: Date } | undefined; - const { unreadNotLoaded } = RoomHistoryManager.getRoom(rid); - - if (!lastReadRecord && unreadNotLoaded.get() === 0) { - lastReadRecord = { ts: new Date(0) }; - } - - room.unreadSince.set(lastReadRecord || unreadNotLoaded.get() > 0 ? subscription.ls : undefined); - - if (!lastReadRecord) { - return; - } - - const firstUnreadRecord = ChatMessage.findOne( - { - 'rid': subscription.rid, - 'ts': { - $gt: lastReadRecord.ts, - }, - 'u._id': { - $ne: Meteor.userId() ?? undefined, - }, - }, - { - sort: { - ts: 1, - }, - }, - ); - - if (firstUnreadRecord) { - room.unreadFirstId = firstUnreadRecord._id; - document.querySelector('.message.first-unread')?.classList.remove('first-unread'); - document.querySelector(`.message[data-id="${firstUnreadRecord._id}"]`)?.classList.add('first-unread'); - } - } -} - -export const readMessage = new ReadMessage(); diff --git a/apps/meteor/app/ui-utils/server/Message.ts b/apps/meteor/app/ui-utils/server/Message.ts index ab78c2fe19a7..18cf842b1993 100644 --- a/apps/meteor/app/ui-utils/server/Message.ts +++ b/apps/meteor/app/ui-utils/server/Message.ts @@ -1,11 +1,11 @@ -import { Meteor } from 'meteor/meteor'; -import { escapeHTML } from '@rocket.chat/string-helpers'; import type { IMessage } from '@rocket.chat/core-typings'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; -import { MessageTypes } from '../lib/MessageTypes'; -import { settings } from '../../settings/server'; import { trim } from '../../../lib/utils/stringUtils'; import { i18n } from '../../../server/lib/i18n'; +import { settings } from '../../settings/server'; +import { MessageTypes } from '../lib/MessageTypes'; export const Message = { parse(msg: IMessage, language: string) { diff --git a/apps/meteor/app/ui/client/lib/ChatMessages.ts b/apps/meteor/app/ui/client/lib/ChatMessages.ts index e71c694c7795..4a4b04f11833 100644 --- a/apps/meteor/app/ui/client/lib/ChatMessages.ts +++ b/apps/meteor/app/ui/client/lib/ChatMessages.ts @@ -1,22 +1,25 @@ import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import { isVideoConfMessage } from '@rocket.chat/core-typings'; import type { UIEvent } from 'react'; -import { - setHighlightMessage, - clearHighlightMessage, -} from '../../../../client/views/room/MessageList/providers/messageHighlightSubscription'; import type { ChatAPI, ComposerAPI, DataAPI, UploadsAPI } from '../../../../client/lib/chats/ChatAPI'; -import { uploadFiles } from '../../../../client/lib/chats/flows/uploadFiles'; -import { processSlashCommand } from '../../../../client/lib/chats/flows/processSlashCommand'; -import { requestMessageDeletion } from '../../../../client/lib/chats/flows/requestMessageDeletion'; +import { createDataAPI } from '../../../../client/lib/chats/data'; import { processMessageEditing } from '../../../../client/lib/chats/flows/processMessageEditing'; -import { processTooLongMessage } from '../../../../client/lib/chats/flows/processTooLongMessage'; import { processSetReaction } from '../../../../client/lib/chats/flows/processSetReaction'; -import { sendMessage } from '../../../../client/lib/chats/flows/sendMessage'; -import { UserAction } from './UserAction'; +import { processSlashCommand } from '../../../../client/lib/chats/flows/processSlashCommand'; +import { processTooLongMessage } from '../../../../client/lib/chats/flows/processTooLongMessage'; import { replyBroadcast } from '../../../../client/lib/chats/flows/replyBroadcast'; -import { createDataAPI } from '../../../../client/lib/chats/data'; +import { requestMessageDeletion } from '../../../../client/lib/chats/flows/requestMessageDeletion'; +import { sendMessage } from '../../../../client/lib/chats/flows/sendMessage'; +import { uploadFiles } from '../../../../client/lib/chats/flows/uploadFiles'; +import { ReadStateManager } from '../../../../client/lib/chats/readStateManager'; import { createUploadsAPI } from '../../../../client/lib/chats/uploads'; +import { + setHighlightMessage, + clearHighlightMessage, +} from '../../../../client/views/room/MessageList/providers/messageHighlightSubscription'; +import * as ActionManager from '../../../ui-message/client/ActionManager'; +import { UserAction } from './UserAction'; type DeepWritable = T extends (...args: any) => any ? T @@ -36,8 +39,12 @@ export class ChatMessages implements ChatAPI { public data: DataAPI; + public readStateManager: ReadStateManager; + public uploads: UploadsAPI; + public ActionManager: any; + public userCard: { open(username: string): (event: UIEvent) => void; close(): void }; public emojiPicker: { @@ -60,7 +67,12 @@ export class ChatMessages implements ChatAPI { } if (!this.currentEditing) { - const lastMessage = await this.data.findLastOwnMessage(); + let lastMessage = await this.data.findLastOwnMessage(); + + // Videoconf messages should not be edited + if (lastMessage && isVideoConfMessage(lastMessage)) { + lastMessage = await this.data.findPreviousOwnMessage(lastMessage); + } if (lastMessage) { await this.data.saveDraft(undefined, this.composer.text); @@ -71,7 +83,12 @@ export class ChatMessages implements ChatAPI { } const currentMessage = await this.data.findMessageByID(this.currentEditing.mid); - const previousMessage = currentMessage ? await this.data.findPreviousOwnMessage(currentMessage) : undefined; + let previousMessage = currentMessage ? await this.data.findPreviousOwnMessage(currentMessage) : undefined; + + // Videoconf messages should not be edited + if (previousMessage && isVideoConfMessage(previousMessage)) { + previousMessage = await this.data.findPreviousOwnMessage(previousMessage); + } if (previousMessage) { await this.messageEditing.editMessage(previousMessage); @@ -86,7 +103,12 @@ export class ChatMessages implements ChatAPI { } const currentMessage = await this.data.findMessageByID(this.currentEditing.mid); - const nextMessage = currentMessage ? await this.data.findNextOwnMessage(currentMessage) : undefined; + let nextMessage = currentMessage ? await this.data.findNextOwnMessage(currentMessage) : undefined; + + // Videoconf messages should not be edited + if (nextMessage && isVideoConfMessage(nextMessage)) { + nextMessage = await this.data.findNextOwnMessage(nextMessage); + } if (nextMessage) { await this.messageEditing.editMessage(nextMessage, { cursorAtStart: true }); @@ -128,11 +150,14 @@ export class ChatMessages implements ChatAPI { this.uid = params.uid; this.data = createDataAPI({ rid, tmid }); this.uploads = createUploadsAPI({ rid, tmid }); + this.ActionManager = ActionManager; const unimplemented = () => { throw new Error('Flow is not implemented'); }; + this.readStateManager = new ReadStateManager(rid); + this.userCard = { open: unimplemented, close: unimplemented, diff --git a/apps/meteor/app/ui/client/lib/KonchatNotification.ts b/apps/meteor/app/ui/client/lib/KonchatNotification.ts index 38e9a807fd8c..1af8ca18d337 100644 --- a/apps/meteor/app/ui/client/lib/KonchatNotification.ts +++ b/apps/meteor/app/ui/client/lib/KonchatNotification.ts @@ -1,19 +1,19 @@ import type { IMessage, IRoom, IUser, RoomType } from '@rocket.chat/core-typings'; -import { ReactiveVar } from 'meteor/reactive-var'; import { Random } from '@rocket.chat/random'; import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { RoomManager } from '../../../../client/lib/RoomManager'; import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived'; -import { getUserPreference } from '../../../utils/client'; -import { getUserAvatarURL } from '../../../utils/client/getUserAvatarURL'; -import { e2e } from '../../../e2e/client'; -import { ChatSubscription } from '../../../models/client'; -import { CustomSounds } from '../../../custom-sounds/client/lib/CustomSounds'; import { getAvatarAsPng } from '../../../../client/lib/utils/getAvatarAsPng'; +import { router } from '../../../../client/providers/RouterProvider'; import { stripTags } from '../../../../lib/utils/stringUtils'; -import { RoomManager } from '../../../../client/lib/RoomManager'; +import { CustomSounds } from '../../../custom-sounds/client/lib/CustomSounds'; +import { e2e } from '../../../e2e/client'; +import { ChatSubscription } from '../../../models/client'; +import { getUserPreference } from '../../../utils/client'; +import { getUserAvatarURL } from '../../../utils/client/getUserAvatarURL'; import { sdk } from '../../../utils/client/lib/SDKClient'; -import { router } from '../../../../client/providers/RouterProvider'; declare global { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/ui/client/lib/UserAction.ts b/apps/meteor/app/ui/client/lib/UserAction.ts index f49852155f04..ab58d641c149 100644 --- a/apps/meteor/app/ui/client/lib/UserAction.ts +++ b/apps/meteor/app/ui/client/lib/UserAction.ts @@ -1,10 +1,10 @@ +import type { IExtras, IRoomActivity, IActionsObject, IUser } from '@rocket.chat/core-typings'; +import { debounce } from 'lodash'; import { Meteor } from 'meteor/meteor'; import { ReactiveDict } from 'meteor/reactive-dict'; -import { debounce } from 'lodash'; -import type { IExtras, IRoomActivity, IActionsObject, IUser } from '@rocket.chat/core-typings'; -import { settings } from '../../../settings/client'; import { Notifications } from '../../../notifications/client'; +import { settings } from '../../../settings/client'; const TIMEOUT = 15000; const RENEW = TIMEOUT / 3; diff --git a/apps/meteor/app/ui/client/lib/recorderjs/AudioEncoder.ts b/apps/meteor/app/ui/client/lib/recorderjs/AudioEncoder.ts index 7f7dcb169db4..2360c69e354a 100644 --- a/apps/meteor/app/ui/client/lib/recorderjs/AudioEncoder.ts +++ b/apps/meteor/app/ui/client/lib/recorderjs/AudioEncoder.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { Emitter } from '@rocket.chat/emitter'; +import { Meteor } from 'meteor/meteor'; export class AudioEncoder extends Emitter { private worker: Worker; diff --git a/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts b/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts index d8bd8cbb323c..85a7853c1ece 100644 --- a/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts +++ b/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts @@ -1,5 +1,5 @@ -import { AudioEncoder } from './AudioEncoder'; import { settings } from '../../../../settings/client'; +import { AudioEncoder } from './AudioEncoder'; export class AudioRecorder { private audioContext: AudioContext | undefined; diff --git a/apps/meteor/app/ui/client/views/app/lib/CommonRoomTemplateInstance.ts b/apps/meteor/app/ui/client/views/app/lib/CommonRoomTemplateInstance.ts deleted file mode 100644 index 3ec550c5bb87..000000000000 --- a/apps/meteor/app/ui/client/views/app/lib/CommonRoomTemplateInstance.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ContextType } from 'react'; - -import type { ToolboxContextValue } from '../../../../../../client/views/room/contexts/ToolboxContext'; -import type { ChatContext } from '../../../../../../client/views/room/contexts/ChatContext'; - -export type CommonRoomTemplateInstance = { - data: { - rid: string; - tabBar: ToolboxContextValue; - chatContext: ContextType; - }; -}; diff --git a/apps/meteor/app/ui/client/views/app/photoswipeContent.ts b/apps/meteor/app/ui/client/views/app/photoswipeContent.ts index 493d7c60f081..5c76a6b15d18 100644 --- a/apps/meteor/app/ui/client/views/app/photoswipeContent.ts +++ b/apps/meteor/app/ui/client/views/app/photoswipeContent.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { escapeHTML } from '@rocket.chat/string-helpers'; +import { Meteor } from 'meteor/meteor'; import type PhotoSwipe from 'photoswipe'; import PhotoSwipeUIDefault from 'photoswipe/dist/photoswipe-ui-default'; diff --git a/apps/meteor/app/user-status/client/lib/customUserStatus.js b/apps/meteor/app/user-status/client/lib/customUserStatus.js index f202e7f97b76..8d429d22c3ec 100644 --- a/apps/meteor/app/user-status/client/lib/customUserStatus.js +++ b/apps/meteor/app/user-status/client/lib/customUserStatus.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { userStatus } from './userStatus'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { userStatus } from './userStatus'; userStatus.packages.customUserStatus = { list: [], diff --git a/apps/meteor/app/user-status/client/notifications/deleteCustomUserStatus.js b/apps/meteor/app/user-status/client/notifications/deleteCustomUserStatus.js index 21a14477f883..24d503d57d72 100644 --- a/apps/meteor/app/user-status/client/notifications/deleteCustomUserStatus.js +++ b/apps/meteor/app/user-status/client/notifications/deleteCustomUserStatus.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { deleteCustomUserStatus } from '../lib/customUserStatus'; import { Notifications } from '../../../notifications/client'; +import { deleteCustomUserStatus } from '../lib/customUserStatus'; Meteor.startup(() => Notifications.onLogged('deleteCustomUserStatus', (data) => deleteCustomUserStatus(data.userStatusData))); diff --git a/apps/meteor/app/user-status/client/notifications/updateCustomUserStatus.js b/apps/meteor/app/user-status/client/notifications/updateCustomUserStatus.js index 4c780550a357..f5949b038948 100644 --- a/apps/meteor/app/user-status/client/notifications/updateCustomUserStatus.js +++ b/apps/meteor/app/user-status/client/notifications/updateCustomUserStatus.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { updateCustomUserStatus } from '../lib/customUserStatus'; import { Notifications } from '../../../notifications/client'; +import { updateCustomUserStatus } from '../lib/customUserStatus'; Meteor.startup(() => Notifications.onLogged('updateCustomUserStatus', (data) => updateCustomUserStatus(data.userStatusData))); diff --git a/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts b/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts index b0ab4d17e3ac..f58fa9551f4c 100644 --- a/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { CustomUserStatus } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; +import { CustomUserStatus } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/user-status/server/methods/getUserStatusText.ts b/apps/meteor/app/user-status/server/methods/getUserStatusText.ts index e8e5b5a34f29..5aa80627d562 100644 --- a/apps/meteor/app/user-status/server/methods/getUserStatusText.ts +++ b/apps/meteor/app/user-status/server/methods/getUserStatusText.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { getStatusText } from '../../../lib/server'; +import { getStatusText } from '../../../lib/server/functions/getStatusText'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts b/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts index ad4be7276dcb..b6a98895bc2c 100644 --- a/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { CustomUserStatus } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ICustomUserStatus } from '@rocket.chat/core-typings'; import type { InsertionModel } from '@rocket.chat/model-typings'; +import { CustomUserStatus } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { trim } from '../../../../lib/utils/stringUtils'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts b/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts index 39fd0a53d6c7..bb11a1be73bd 100644 --- a/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { CustomUserStatus } from '@rocket.chat/models'; import type { ICustomUserStatus } from '@rocket.chat/core-typings'; +import { CustomUserStatus } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/user-status/server/methods/setUserStatus.ts b/apps/meteor/app/user-status/server/methods/setUserStatus.ts index 4c009aff234b..32db93e722fb 100644 --- a/apps/meteor/app/user-status/server/methods/setUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/setUserStatus.ts @@ -1,12 +1,12 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; +import { Presence } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { Presence } from '@rocket.chat/core-services'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; import { RateLimiter } from '../../../lib/server'; import { setStatusText } from '../../../lib/server/functions/setStatusText'; +import { settings } from '../../../settings/server'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/utils/client/getRoomAvatarURL.ts b/apps/meteor/app/utils/client/getRoomAvatarURL.ts index 8f7dd9dec533..061f62107b44 100644 --- a/apps/meteor/app/utils/client/getRoomAvatarURL.ts +++ b/apps/meteor/app/utils/client/getRoomAvatarURL.ts @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { getAvatarURL } from './getAvatarURL'; import { settings } from '../../settings/client'; +import { getAvatarURL } from './getAvatarURL'; export const getRoomAvatarURL = ({ roomId, cache = '' }: { roomId: IRoom['_id']; cache: IRoom['avatarETag'] }) => { const externalSource = (settings.get('Accounts_RoomAvatarExternalProviderUrl') || '').trim().replace(/\/$/, ''); diff --git a/apps/meteor/app/utils/client/getUserAvatarURL.ts b/apps/meteor/app/utils/client/getUserAvatarURL.ts index e154145a1ec4..1a825a44fc27 100644 --- a/apps/meteor/app/utils/client/getUserAvatarURL.ts +++ b/apps/meteor/app/utils/client/getUserAvatarURL.ts @@ -1,5 +1,5 @@ -import { getAvatarURL } from './getAvatarURL'; import { settings } from '../../settings/client'; +import { getAvatarURL } from './getAvatarURL'; export const getUserAvatarURL = function (username: string, cache = ''): string | undefined { const externalSource = (settings.get('Accounts_AvatarExternalProviderUrl') || '').trim().replace(/\/$/, ''); diff --git a/apps/meteor/app/utils/client/lib/RestApiClient.ts b/apps/meteor/app/utils/client/lib/RestApiClient.ts index 457bf8fd50c3..c5e12250b441 100644 --- a/apps/meteor/app/utils/client/lib/RestApiClient.ts +++ b/apps/meteor/app/utils/client/lib/RestApiClient.ts @@ -1,9 +1,9 @@ import { RestClient } from '@rocket.chat/api-client'; -import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Meteor } from 'meteor/meteor'; -import { baseURI } from '../../../../client/lib/baseURI'; import { invokeTwoFactorModal } from '../../../../client/lib/2fa/process2faReturn'; +import { baseURI } from '../../../../client/lib/baseURI'; class RestApiClient extends RestClient { getCredentials(): @@ -37,7 +37,7 @@ APIClient.handleTwoFactorChallenge(invokeTwoFactorModal); * This middleware will throw the error object instead. * */ -APIClient.use(async function (request, next) { +APIClient.use(async (request, next) => { try { return await next(...request); } catch (error) { diff --git a/apps/meteor/app/utils/client/lib/SDKClient.ts b/apps/meteor/app/utils/client/lib/SDKClient.ts index baf06c9a5389..e9e20bbe658b 100644 --- a/apps/meteor/app/utils/client/lib/SDKClient.ts +++ b/apps/meteor/app/utils/client/lib/SDKClient.ts @@ -1,11 +1,11 @@ import type { RestClientInterface } from '@rocket.chat/api-client'; import type { SDK } from '@rocket.chat/ddp-client/src/DDPSDK'; import type { ClientStream } from '@rocket.chat/ddp-client/src/types/ClientStream'; -import { Emitter } from '@rocket.chat/emitter'; import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; +import { Emitter } from '@rocket.chat/emitter'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { DDPCommon } from 'meteor/ddp-common'; import { Meteor } from 'meteor/meteor'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { APIClient } from './RestApiClient'; diff --git a/apps/meteor/app/utils/client/restrictions.ts b/apps/meteor/app/utils/client/restrictions.ts index 8d57091891ee..261eddf4467d 100644 --- a/apps/meteor/app/utils/client/restrictions.ts +++ b/apps/meteor/app/utils/client/restrictions.ts @@ -1,5 +1,5 @@ -import { fileUploadIsValidContentTypeFromSettings } from '../lib/restrictions'; import { settings } from '../../settings/client'; +import { fileUploadIsValidContentTypeFromSettings } from '../lib/restrictions'; export const fileUploadIsValidContentType = function (type: string, customWhiteList?: string): boolean { const blackList = settings.get('FileUpload_MediaTypeBlackList'); diff --git a/apps/meteor/app/utils/lib/i18n.ts b/apps/meteor/app/utils/lib/i18n.ts index 309585ee284b..13d5c667709d 100644 --- a/apps/meteor/app/utils/lib/i18n.ts +++ b/apps/meteor/app/utils/lib/i18n.ts @@ -5,9 +5,7 @@ import { isObject } from '../../../lib/utils/isObject'; export const i18n = i18next.use(sprintf); -export const addSprinfToI18n = function (t: (typeof i18n)['t']): typeof t & { - (key: string, ...replaces: any): string; -} { +export const addSprinfToI18n = function (t: (typeof i18n)['t']) { return function (key: string, ...replaces: any): string { if (replaces[0] === undefined || isObject(replaces[0])) { return t(key, ...replaces); diff --git a/apps/meteor/app/utils/lib/restrictions.ts b/apps/meteor/app/utils/lib/restrictions.ts index 29263e5b825d..ebebe113f31e 100644 --- a/apps/meteor/app/utils/lib/restrictions.ts +++ b/apps/meteor/app/utils/lib/restrictions.ts @@ -6,7 +6,7 @@ export const fileUploadMediaWhiteList = function (customWhiteList: string): stri if (!mediaTypeWhiteList || mediaTypeWhiteList === '*') { return; } - return _.map(mediaTypeWhiteList.split(','), function (item) { + return _.map(mediaTypeWhiteList.split(','), (item) => { return item.trim(); }); }; @@ -25,7 +25,7 @@ const isTypeOnList = function (type: string, list: string[]): boolean | undefine return true; } const wildCardGlob = '/*'; - const wildcards = _.filter(list, function (item) { + const wildcards = _.filter(list, (item) => { return item.indexOf(wildCardGlob) > 0; }); if (_.contains(wildcards, type.replace(/(\/.*)$/, wildCardGlob))) { diff --git a/apps/meteor/app/utils/lib/slashCommand.ts b/apps/meteor/app/utils/lib/slashCommand.ts index db0cb2d88e71..f4e2cc1b359a 100644 --- a/apps/meteor/app/utils/lib/slashCommand.ts +++ b/apps/meteor/app/utils/lib/slashCommand.ts @@ -1,4 +1,3 @@ -import { Meteor } from 'meteor/meteor'; import type { IMessage, SlashCommand, @@ -7,6 +6,7 @@ import type { SlashCommandPreviewItem, SlashCommandPreviews, } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; interface ISlashCommandAddParams { command: string; diff --git a/apps/meteor/app/utils/lib/templateVarHandler.ts b/apps/meteor/app/utils/lib/templateVarHandler.ts index 54f28ebee7a5..cf8279930bda 100644 --- a/apps/meteor/app/utils/lib/templateVarHandler.ts +++ b/apps/meteor/app/utils/lib/templateVarHandler.ts @@ -1,4 +1,4 @@ -import { Logger } from '../../../server/lib/logger/Logger'; +import { Logger } from '@rocket.chat/logger'; const logger = new Logger('TemplateVarHandler'); diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index e80339378932..e97b3accfbf5 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.3.8" + "version": "6.4.0-rc.5" } diff --git a/apps/meteor/app/utils/server/functions/normalizeMessageFileUpload.ts b/apps/meteor/app/utils/server/functions/normalizeMessageFileUpload.ts index f00bda5a501b..097bbc4e9eb8 100644 --- a/apps/meteor/app/utils/server/functions/normalizeMessageFileUpload.ts +++ b/apps/meteor/app/utils/server/functions/normalizeMessageFileUpload.ts @@ -1,8 +1,8 @@ -import { Uploads } from '@rocket.chat/models'; import type { IMessage } from '@rocket.chat/core-typings'; +import { Uploads } from '@rocket.chat/models'; -import { getURL } from '../getURL'; import { FileUpload } from '../../../file-upload/server'; +import { getURL } from '../getURL'; export const normalizeMessageFileUpload = async (message: Omit): Promise> => { if (message.file && !message.fileUpload) { diff --git a/apps/meteor/app/utils/server/getUserAvatarURL.ts b/apps/meteor/app/utils/server/getUserAvatarURL.ts index e45b808f832a..b83efea1d842 100644 --- a/apps/meteor/app/utils/server/getUserAvatarURL.ts +++ b/apps/meteor/app/utils/server/getUserAvatarURL.ts @@ -1,5 +1,5 @@ -import { getAvatarURL } from './getAvatarURL'; import { settings } from '../../settings/server'; +import { getAvatarURL } from './getAvatarURL'; export const getUserAvatarURL = function (username: string, cache = ''): string | undefined { const externalSource = (settings.get('Accounts_AvatarExternalProviderUrl') || '').trim().replace(/\/$/, ''); diff --git a/apps/meteor/app/utils/server/getUserNotificationPreference.ts b/apps/meteor/app/utils/server/getUserNotificationPreference.ts index edb45cb64cfa..2c70a7a11a18 100644 --- a/apps/meteor/app/utils/server/getUserNotificationPreference.ts +++ b/apps/meteor/app/utils/server/getUserNotificationPreference.ts @@ -1,5 +1,5 @@ -import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { settings } from '../../settings/server'; diff --git a/apps/meteor/app/utils/server/index.ts b/apps/meteor/app/utils/server/index.ts deleted file mode 100644 index e144683b4626..000000000000 --- a/apps/meteor/app/utils/server/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; -export { Info } from '../rocketchat.info'; -export { getUserPreference } from './lib/getUserPreference'; -export { fileUploadIsValidContentType } from './restrictions'; -export { isDocker } from './functions/isDocker'; -export { getMongoInfo } from './functions/getMongoInfo'; -export { slashCommands } from './slashCommand'; -export { getUserNotificationPreference } from './getUserNotificationPreference'; -export { getAvatarColor } from '../lib/getAvatarColor'; -export { getURL } from './getURL'; -export { getValidRoomName } from './lib/getValidRoomName'; -export { placeholders } from './placeholders'; -export { secondsToHHMMSS } from '../../../lib/utils/secondsToHHMMSS'; diff --git a/apps/meteor/app/utils/server/lib/getUserPreference.ts b/apps/meteor/app/utils/server/lib/getUserPreference.ts index 94670ba0daa0..683985e5626f 100644 --- a/apps/meteor/app/utils/server/lib/getUserPreference.ts +++ b/apps/meteor/app/utils/server/lib/getUserPreference.ts @@ -1,5 +1,5 @@ -import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/utils/server/lib/getValidRoomName.ts b/apps/meteor/app/utils/server/lib/getValidRoomName.ts index 79e4131f830d..5e4be52b6aec 100644 --- a/apps/meteor/app/utils/server/lib/getValidRoomName.ts +++ b/apps/meteor/app/utils/server/lib/getValidRoomName.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import limax from 'limax'; -import { escapeHTML } from '@rocket.chat/string-helpers'; import { Rooms } from '@rocket.chat/models'; +import { escapeHTML } from '@rocket.chat/string-helpers'; +import limax from 'limax'; +import { Meteor } from 'meteor/meteor'; -import { settings } from '../../../settings/server'; import { validateName } from '../../../lib/server/functions/validateName'; +import { settings } from '../../../settings/server'; export const getValidRoomName = async ( displayName: string, diff --git a/apps/meteor/app/utils/server/restrictions.ts b/apps/meteor/app/utils/server/restrictions.ts index 543590dea914..ca524b09d351 100644 --- a/apps/meteor/app/utils/server/restrictions.ts +++ b/apps/meteor/app/utils/server/restrictions.ts @@ -1,5 +1,5 @@ -import { fileUploadIsValidContentTypeFromSettings } from '../lib/restrictions'; import { settings } from '../../settings/server'; +import { fileUploadIsValidContentTypeFromSettings } from '../lib/restrictions'; export const fileUploadIsValidContentType = function (type: string, customWhiteList?: string): boolean { const blackList = settings.get('FileUpload_MediaTypeBlackList'); diff --git a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts index 852bb74b6765..16a3034c0bd3 100644 --- a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts +++ b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts @@ -1,13 +1,13 @@ -import semver from 'semver'; -import { Settings, Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; +import { Settings, Users } from '@rocket.chat/models'; +import semver from 'semver'; -import { getNewUpdates } from './getNewUpdates'; +import { i18n } from '../../../../server/lib/i18n'; +import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; import { settings } from '../../../settings/server'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; import logger from '../logger'; -import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; -import { i18n } from '../../../../server/lib/i18n'; +import { getNewUpdates } from './getNewUpdates'; // import getNewUpdates from '../sampleUpdateData'; const getMessagesToSendToAdmins = async ( diff --git a/apps/meteor/app/version-check/server/functions/getNewUpdates.ts b/apps/meteor/app/version-check/server/functions/getNewUpdates.ts index 10b823f75ed3..ac0c0e443453 100644 --- a/apps/meteor/app/version-check/server/functions/getNewUpdates.ts +++ b/apps/meteor/app/version-check/server/functions/getNewUpdates.ts @@ -1,11 +1,11 @@ import os from 'os'; import { Settings } from '@rocket.chat/models'; -import { check, Match } from 'meteor/check'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { check, Match } from 'meteor/check'; -import { Info } from '../../../utils/server'; import { getWorkspaceAccessToken } from '../../../cloud/server'; +import { Info } from '../../../utils/rocketchat.info'; export const getNewUpdates = async () => { try { diff --git a/apps/meteor/app/version-check/server/index.ts b/apps/meteor/app/version-check/server/index.ts index fe5e4199c2cb..6736e527f3d8 100644 --- a/apps/meteor/app/version-check/server/index.ts +++ b/apps/meteor/app/version-check/server/index.ts @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; import { cronJobs } from '@rocket.chat/cron'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../settings/server'; import { checkVersionUpdate } from './functions/checkVersionUpdate'; diff --git a/apps/meteor/app/version-check/server/logger.ts b/apps/meteor/app/version-check/server/logger.ts index dc35a0b0d50d..b12e06d9a14e 100644 --- a/apps/meteor/app/version-check/server/logger.ts +++ b/apps/meteor/app/version-check/server/logger.ts @@ -1,3 +1,3 @@ -import { Logger } from '../../logger/server'; +import { Logger } from '@rocket.chat/logger'; export default new Logger('VersionCheck'); diff --git a/apps/meteor/app/version-check/server/methods/banner_dismiss.ts b/apps/meteor/app/version-check/server/methods/banner_dismiss.ts index b2f95212d230..960379d1a99b 100644 --- a/apps/meteor/app/version-check/server/methods/banner_dismiss.ts +++ b/apps/meteor/app/version-check/server/methods/banner_dismiss.ts @@ -1,6 +1,6 @@ +import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { Users } from '@rocket.chat/models'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/videobridge/client/index.ts b/apps/meteor/app/videobridge/client/index.ts deleted file mode 100644 index cd95da0a7d92..000000000000 --- a/apps/meteor/app/videobridge/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './tabBar'; diff --git a/apps/meteor/app/videobridge/client/tabBar.tsx b/apps/meteor/app/videobridge/client/tabBar.tsx deleted file mode 100644 index 23fb71b09e35..000000000000 --- a/apps/meteor/app/videobridge/client/tabBar.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { useMemo, lazy } from 'react'; -import { useStableArray, useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetting, useUser, useTranslation, usePermission } from '@rocket.chat/ui-contexts'; -import { isRoomFederated } from '@rocket.chat/core-typings'; - -import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging } from '../../../client/contexts/VideoConfContext'; -import type { ToolboxActionConfig } from '../../../client/views/room/lib/Toolbox'; -import { addAction } from '../../../client/views/room/lib/Toolbox'; -import { VideoConfManager } from '../../../client/lib/VideoConfManager'; -import { useVideoConfWarning } from '../../../client/views/room/contextualBar/VideoConference/hooks/useVideoConfWarning'; -import { useHasLicenseModule } from '../../../ee/client/hooks/useHasLicenseModule'; - -addAction('calls', ({ room }) => { - const t = useTranslation(); - const hasLicense = useHasLicenseModule('videoconference-enterprise'); - const federated = isRoomFederated(room); - - return useMemo( - () => - hasLicense - ? { - groups: ['channel', 'group', 'team'], - id: 'calls', - icon: 'phone', - title: 'Calls', - ...(federated && { - 'data-tooltip': t('Video_Call_unavailable_for_this_type_of_room'), - 'disabled': true, - }), - template: lazy(() => import('../../../client/views/room/contextualBar/VideoConference/VideoConfList')), - order: 999, - } - : null, - [hasLicense, federated, t], - ); -}); - -addAction('start-call', ({ room }) => { - const t = useTranslation(); - const user = useUser(); - const dispatchWarning = useVideoConfWarning(); - const dispatchPopup = useVideoConfDispatchOutgoing(); - const isCalling = useVideoConfIsCalling(); - const isRinging = useVideoConfIsRinging(); - const federated = isRoomFederated(room); - const canPostReadOnly = usePermission('post-readonly', room._id); - const canStartCall = usePermission('call-management', room._id); - - const ownUser = room.uids && room.uids.length === 1; - - // Only disable video conf if the settings are explicitly FALSE - any falsy value counts as true - const enabledDMs = useSetting('VideoConf_Enable_DMs') !== false; - const enabledChannel = useSetting('VideoConf_Enable_Channels') !== false; - const enabledTeams = useSetting('VideoConf_Enable_Teams') !== false; - const enabledGroups = useSetting('VideoConf_Enable_Groups') !== false; - const enabledLiveChat = useSetting('Omnichannel_call_provider') === 'default-provider'; - - const live = room?.streamingOptions && room.streamingOptions.type === 'call'; - const enabled = enabledDMs || enabledChannel || enabledTeams || enabledGroups || enabledLiveChat; - - const enableOption = enabled && canStartCall && (!user?.username || !room.muted?.includes(user.username)); - - const groups = useStableArray( - [ - enabledDMs && 'direct', - enabledDMs && 'direct_multiple', - enabledGroups && 'group', - enabledLiveChat && 'live', - enabledTeams && 'team', - enabledChannel && 'channel', - ].filter(Boolean) as ToolboxActionConfig['groups'], - ); - - const handleOpenVideoConf = useMutableCallback(async (): Promise => { - if (isCalling || isRinging) { - return; - } - - try { - await VideoConfManager.loadCapabilities(); - dispatchPopup({ rid: room._id }); - } catch (error: any) { - dispatchWarning(error.error); - } - }); - - return useMemo( - () => - enableOption && !ownUser - ? { - groups, - id: 'start-call', - title: 'Call', - icon: 'phone', - action: handleOpenVideoConf, - ...((federated || (room.ro && !canPostReadOnly)) && { - 'data-tooltip': t('Video_Call_unavailable_for_this_type_of_room'), - 'disabled': true, - }), - full: true, - order: live ? -1 : 4, - featured: true, - } - : null, - [groups, enableOption, live, handleOpenVideoConf, ownUser, canPostReadOnly, federated, t, room.ro], - ); -}); diff --git a/apps/meteor/app/voip/server/startup.ts b/apps/meteor/app/voip/server/startup.ts index 5a9982983490..d96206f12985 100644 --- a/apps/meteor/app/voip/server/startup.ts +++ b/apps/meteor/app/voip/server/startup.ts @@ -2,7 +2,7 @@ import { Voip } from '@rocket.chat/core-services'; import { settings } from '../../settings/server'; -settings.watch('VoIP_Enabled', async function (value: boolean) { +settings.watch('VoIP_Enabled', async (value: boolean) => { try { if (value) { await Voip.init(); @@ -16,7 +16,7 @@ settings.watch('VoIP_Enabled', async function (value: boolean) { settings.changeMultiple( ['VoIP_Management_Server_Host', 'VoIP_Management_Server_Port', 'VoIP_Management_Server_Username', 'VoIP_Management_Server_Password'], - async function (_values) { + async (_values) => { // Here, if 4 settings are changed at once, we're getting 4 diff callbacks. The good part is that all callbacks are fired almost instantly // So to avoid stopping/starting voip too often, we debounce the call and restart 1 second after the last setting has reached us. if (settings.get('VoIP_Enabled')) { diff --git a/apps/meteor/app/webdav/client/actionButton.ts b/apps/meteor/app/webdav/client/actionButton.ts index 84e667940747..9a2f8152e5c0 100644 --- a/apps/meteor/app/webdav/client/actionButton.ts +++ b/apps/meteor/app/webdav/client/actionButton.ts @@ -1,14 +1,14 @@ import { Meteor } from 'meteor/meteor'; -import { getURL } from '../../utils/client'; +import { imperativeModal } from '../../../client/lib/imperativeModal'; +import { messageArgs } from '../../../client/lib/utils/messageArgs'; +import SaveToWebdav from '../../../client/views/room/webdav/SaveToWebdavModal'; import { WebdavAccounts } from '../../models/client'; import { settings } from '../../settings/client'; import { MessageAction } from '../../ui-utils/client'; -import { messageArgs } from '../../../client/lib/utils/messageArgs'; -import { imperativeModal } from '../../../client/lib/imperativeModal'; -import SaveToWebdav from '../../../client/views/room/webdav/SaveToWebdavModal'; +import { getURL } from '../../utils/client'; -Meteor.startup(function () { +Meteor.startup(() => { MessageAction.addButton({ id: 'webdav-upload', icon: 'upload', diff --git a/apps/meteor/app/webdav/server/lib/uploadFileToWebdav.ts b/apps/meteor/app/webdav/server/lib/uploadFileToWebdav.ts index c7033894c8e4..7b4c7a095148 100644 --- a/apps/meteor/app/webdav/server/lib/uploadFileToWebdav.ts +++ b/apps/meteor/app/webdav/server/lib/uploadFileToWebdav.ts @@ -1,5 +1,5 @@ -import { WebdavAccounts } from '@rocket.chat/models'; import type { IWebdavAccount } from '@rocket.chat/core-typings'; +import { WebdavAccounts } from '@rocket.chat/models'; import { getWebdavCredentials } from './getWebdavCredentials'; import { WebdavClientAdapter } from './webdavClientAdapter'; diff --git a/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts b/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts index 724bfa2b6703..e2c9a9ddc324 100644 --- a/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts +++ b/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts @@ -1,9 +1,9 @@ -import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; -import { WebdavAccounts } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { IWebdavAccountPayload } from '@rocket.chat/core-typings'; +import { WebdavAccounts } from '@rocket.chat/models'; +import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Match, check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; import { WebdavClientAdapter } from '../lib/webdavClientAdapter'; diff --git a/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts b/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts index 676c1ca0637a..346362935d87 100644 --- a/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts +++ b/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { WebdavAccounts } from '@rocket.chat/models'; import type { IWebdavAccount, IWebdavNode } from '@rocket.chat/core-typings'; +import { WebdavAccounts } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; import { getWebdavCredentials } from '../lib/getWebdavCredentials'; diff --git a/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts b/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts index fd4462b14002..f4d944181928 100644 --- a/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts +++ b/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { WebdavAccounts } from '@rocket.chat/models'; import type { IWebdavAccount, IWebdavNode } from '@rocket.chat/core-typings'; +import { WebdavAccounts } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; import { getWebdavCredentials } from '../lib/getWebdavCredentials'; diff --git a/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts b/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts index 801863920c97..1e01937a0e52 100644 --- a/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts +++ b/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts @@ -1,8 +1,8 @@ -import { Meteor } from 'meteor/meteor'; -import { createClient } from 'webdav'; -import { WebdavAccounts } from '@rocket.chat/models'; import type { IWebdavAccount } from '@rocket.chat/core-typings'; +import { WebdavAccounts } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; +import { createClient } from 'webdav'; import { settings } from '../../../settings/server'; import { getWebdavCredentials } from '../lib/getWebdavCredentials'; diff --git a/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts b/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts index 8a9a82835c4a..df6102297cc2 100644 --- a/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts +++ b/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -import { WebdavAccounts } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; import type { IWebdavAccount } from '@rocket.chat/core-typings'; -import type { DeleteResult } from 'mongodb'; +import { WebdavAccounts } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check } from 'meteor/check'; +import { Meteor } from 'meteor/meteor'; +import type { DeleteResult } from 'mongodb'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; diff --git a/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts b/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts index 4eb3f5af3ba1..8a2e1badd27d 100644 --- a/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts +++ b/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts @@ -1,10 +1,10 @@ -import { Meteor } from 'meteor/meteor'; import { MeteorError } from '@rocket.chat/core-services'; import type { IWebdavAccount } from '@rocket.chat/core-typings'; +import { Logger } from '@rocket.chat/logger'; import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; -import { Logger } from '../../../logger/server'; import { uploadFileToWebdav } from '../lib/uploadFileToWebdav'; const logger = new Logger('WebDAV_Upload'); diff --git a/apps/meteor/app/webrtc/client/WebRTCClass.js b/apps/meteor/app/webrtc/client/WebRTCClass.js index 63a5021668ae..721c7591c647 100644 --- a/apps/meteor/app/webrtc/client/WebRTCClass.js +++ b/apps/meteor/app/webrtc/client/WebRTCClass.js @@ -1,17 +1,17 @@ import { Emitter } from '@rocket.chat/emitter'; import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; -import { ChromeScreenShare } from './screenShare'; -import { Notifications } from '../../notifications/client'; -import { settings } from '../../settings/client'; -import { ChatSubscription } from '../../models/client'; -import { WEB_RTC_EVENTS } from '../lib/constants'; -import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; import GenericModal from '../../../client/components/GenericModal'; import { imperativeModal } from '../../../client/lib/imperativeModal'; +import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; +import { ChatSubscription } from '../../models/client'; +import { Notifications } from '../../notifications/client'; +import { settings } from '../../settings/client'; import { t } from '../../utils/lib/i18n'; +import { WEB_RTC_EVENTS } from '../lib/constants'; +import { ChromeScreenShare } from './screenShare'; class WebRTCTransportClass extends Emitter { constructor(webrtcInstance) { @@ -434,7 +434,7 @@ class WebRTCClass { if (this.navigator === 'chrome') { const url = 'https://chrome.google.com/webstore/detail/rocketchat-screen-share/nocfbnnmjnndkbipkabodnheejiegccf'; try { - chrome.webstore.install(url, refresh, function () { + chrome.webstore.install(url, refresh, () => { window.open(url); refresh(); }); @@ -556,7 +556,7 @@ class WebRTCClass { setAudioEnabled(enabled = true) { if (this.localStream != null) { - this.localStream.getAudioTracks().forEach(function (audio) { + this.localStream.getAudioTracks().forEach((audio) => { audio.enabled = enabled; }); this.audioEnabled.set(enabled); @@ -580,7 +580,7 @@ class WebRTCClass { setVideoEnabled(enabled = true) { if (this.localStream != null) { - this.localStream.getVideoTracks().forEach(function (video) { + this.localStream.getVideoTracks().forEach((video) => { video.enabled = enabled; }); this.videoEnabled.set(enabled); @@ -966,8 +966,8 @@ const WebRTC = new (class { } })(); -Meteor.startup(function () { - Tracker.autorun(function () { +Meteor.startup(() => { + Tracker.autorun(() => { if (Meteor.userId()) { Notifications.onUser(WEB_RTC_EVENTS.WEB_RTC, (type, data) => { if (data.room == null) { diff --git a/apps/meteor/app/webrtc/client/actionLink.tsx b/apps/meteor/app/webrtc/client/actionLink.tsx index 696edf542961..f9848518b868 100644 --- a/apps/meteor/app/webrtc/client/actionLink.tsx +++ b/apps/meteor/app/webrtc/client/actionLink.tsx @@ -1,11 +1,11 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { actionLinks } from '../../../client/lib/actionLinks'; +import { dispatchToastMessage } from '../../../client/lib/toast'; import { ChatRoom } from '../../models/client'; import { Notifications } from '../../notifications/client'; -import { dispatchToastMessage } from '../../../client/lib/toast'; -import { t } from '../../utils/lib/i18n'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { t } from '../../utils/lib/i18n'; actionLinks.register('joinLivechatWebRTCCall', (message: IMessage) => { const room = ChatRoom.findOne({ _id: message.rid }); diff --git a/apps/meteor/app/webrtc/client/index.ts b/apps/meteor/app/webrtc/client/index.ts index 32ad3e530423..0dc8337bb8c4 100644 --- a/apps/meteor/app/webrtc/client/index.ts +++ b/apps/meteor/app/webrtc/client/index.ts @@ -1,5 +1,4 @@ import './adapter'; -import './tabBar'; import './actionLink'; export * from './WebRTCClass'; diff --git a/apps/meteor/app/webrtc/client/screenShare.js b/apps/meteor/app/webrtc/client/screenShare.js index e5ccf37def1e..ecb6f93a51d0 100644 --- a/apps/meteor/app/webrtc/client/screenShare.js +++ b/apps/meteor/app/webrtc/client/screenShare.js @@ -25,7 +25,7 @@ export const ChromeScreenShare = { ChromeScreenShare.init(); -window.addEventListener('message', function (e) { +window.addEventListener('message', (e) => { if (e.origin !== window.location.origin) { return; } diff --git a/apps/meteor/app/webrtc/client/tabBar.tsx b/apps/meteor/app/webrtc/client/tabBar.tsx deleted file mode 100644 index 370d1ffc6d6c..000000000000 --- a/apps/meteor/app/webrtc/client/tabBar.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useMemo, useCallback } from 'react'; -import { useSetting } from '@rocket.chat/ui-contexts'; -import { isRoomFederated } from '@rocket.chat/core-typings'; - -import { addAction } from '../../../client/views/room/lib/Toolbox'; -import { sdk } from '../../utils/client/lib/SDKClient'; - -addAction('webRTCVideo', ({ room }) => { - const enabled = useSetting('WebRTC_Enabled') && useSetting('Omnichannel_call_provider') === 'WebRTC' && room.servedBy; - const federated = isRoomFederated(room); - - const handleClick = useCallback(async (): Promise => { - if (!room.callStatus || room.callStatus === 'declined' || room.callStatus === 'ended') { - await sdk.rest.get('/v1/livechat/webrtc.call', { rid: room._id }); - } - window.open(`/meet/${room._id}`, room._id); - }, [room._id, room.callStatus]); - - return useMemo( - () => - enabled - ? { - groups: ['live'], - id: 'webRTCVideo', - title: 'WebRTC_Call', - icon: 'phone', - ...(federated && { - 'data-tooltip': 'Call_unavailable_for_federation', - 'disabled': true, - }), - action: handleClick, - full: true, - order: 4, - } - : null, - [enabled, handleClick, federated], - ); -}); diff --git a/apps/meteor/app/wordpress/client/lib.ts b/apps/meteor/app/wordpress/client/lib.ts index 9d5e063bf251..7dd5215ccc60 100644 --- a/apps/meteor/app/wordpress/client/lib.ts +++ b/apps/meteor/app/wordpress/client/lib.ts @@ -1,10 +1,10 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/client'; import { CustomOAuth } from '../../custom-oauth/client/custom_oauth_client'; +import { settings } from '../../settings/client'; const config: OauthConfig = { serverURL: '', @@ -73,8 +73,8 @@ const fillSettings = _.debounce(async (): Promise => { return result; }, 100); -Meteor.startup(function () { - return Tracker.autorun(function () { +Meteor.startup(() => { + return Tracker.autorun(() => { return fillSettings(); }); }); diff --git a/apps/meteor/app/wordpress/server/lib.ts b/apps/meteor/app/wordpress/server/lib.ts index b1eb87ed6c46..7777ea138221 100644 --- a/apps/meteor/app/wordpress/server/lib.ts +++ b/apps/meteor/app/wordpress/server/lib.ts @@ -1,10 +1,10 @@ +import type { OauthConfig } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; import _ from 'underscore'; -import type { OauthConfig } from '@rocket.chat/core-typings'; -import { settings } from '../../settings/server'; import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { settings } from '../../settings/server'; const config: OauthConfig = { serverURL: '', @@ -89,6 +89,6 @@ const fillSettings = _.debounce(async (): Promise => { return result; }, 1000); -Meteor.startup(function () { +Meteor.startup(() => { return settings.watchByRegex(/(API\_Wordpress\_URL)?(Accounts\_OAuth\_Wordpress\_)?/, () => fillSettings()); }); diff --git a/apps/meteor/client/.eslintrc.json b/apps/meteor/client/.eslintrc.json deleted file mode 100644 index 286fbb957017..000000000000 --- a/apps/meteor/client/.eslintrc.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "root": true, - "extends": ["@rocket.chat/eslint-config/original", "prettier", "plugin:you-dont-need-lodash-underscore/compatible"], - "parser": "@babel/eslint-parser", - "plugins": ["react", "react-hooks", "prettier", "testing-library", "anti-trojan-source"], - "rules": { - "import/named": "error", - "import/order": [ - "error", - { - "newlines-between": "always", - "groups": ["builtin", "external", "internal", ["parent", "sibling", "index"]], - "alphabetize": { - "order": "asc" - } - } - ], - "jsx-quotes": ["error", "prefer-single"], - "new-cap": [ - "error", - { - "capIsNewExceptions": ["HTML.Comment", "HTML.Raw", "HTML.DIV", "SHA256"] - } - ], - "prefer-arrow-callback": [ - "error", - { - "allowNamedFunctions": true - } - ], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": [ - "warn", - { - "additionalHooks": "(useComponentDidUpdate)" - } - ], - "anti-trojan-source/no-bidi": "error" - }, - "settings": { - "import/resolver": { - "node": { - "extensions": [".js", ".ts", ".tsx"] - } - }, - "react": { - "version": "detect" - } - }, - "env": { - "browser": true, - "es6": true - }, - "overrides": [ - { - "files": ["**/*.ts", "**/*.tsx"], - "extends": ["@rocket.chat/eslint-config"], - "plugins": ["react", "react-hooks"], - "rules": { - "@typescript-eslint/naming-convention": [ - "error", - { - "selector": ["function", "parameter", "variable"], - "modifiers": ["destructured"], - "format": null - }, - { - "selector": ["variable"], - "format": ["camelCase", "UPPER_CASE", "PascalCase"], - "leadingUnderscore": "allowSingleOrDouble" - }, - { - "selector": ["function"], - "format": ["camelCase", "PascalCase"], - "leadingUnderscore": "allowSingleOrDouble" - }, - { - "selector": ["parameter"], - "format": ["PascalCase"], - "filter": { - "regex": "Component$", - "match": true - } - }, - { - "selector": ["parameter"], - "format": ["camelCase"], - "leadingUnderscore": "allow" - }, - { - "selector": ["parameter"], - "format": ["camelCase"], - "modifiers": ["unused"], - "leadingUnderscore": "require" - }, - { - "selector": ["interface"], - "format": ["PascalCase"], - "custom": { - "regex": "^I[A-Z]", - "match": true - } - } - ], - "@typescript-eslint/no-extra-parens": "off", - "import/order": [ - "error", - { - "newlines-between": "always", - "groups": ["builtin", "external", "internal", ["parent", "sibling", "index"]], - "alphabetize": { - "order": "asc" - } - } - ], - "new-cap": [ - "error", - { - "capIsNewExceptions": ["HTML.Comment", "HTML.Raw", "HTML.DIV", "SHA256"] - } - ], - "no-empty-function": "off", - "no-extra-parens": "off", - "prefer-arrow-callback": [ - "error", - { - "allowNamedFunctions": true - } - ], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": [ - "warn", - { - "additionalHooks": "(useComponentDidUpdate)" - } - ] - }, - "env": { - "browser": true, - "es6": true - }, - "settings": { - "import/resolver": { - "node": { - "extensions": [".js", ".ts", ".tsx"] - } - }, - "react": { - "version": "detect" - } - } - }, - { - "files": ["**/*.stories.js", "**/*.stories.jsx", "**/*.stories.ts", "**/*.stories.tsx"], - "rules": { - "react/display-name": "off", - "react/no-multi-comp": "off" - } - }, - { - "files": ["**/*.tests.js", "**/*.tests.ts", "**/*.spec.ts", "**/*.spec.tsx"], - "extends": ["plugin:testing-library/react"], - "rules": { - "testing-library/no-await-sync-events": "warn", - "testing-library/no-manual-cleanup": "warn", - "testing-library/prefer-explicit-assert": "warn", - "testing-library/prefer-user-event": "warn" - }, - "env": { - "mocha": true - } - }, - { - "files": ["**/*.stories.ts", "**/*.stories.tsx"], - "rules": { - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "off" - } - } - ] -} diff --git a/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx b/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx index f347f1717dfb..6a97f18a7936 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx @@ -1,21 +1,16 @@ import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; -/* eslint-disable new-cap */ -// import { Banner, Icon } from '@rocket.chat/fuselage'; -// import { kitContext, UiKitBanner as renderUiKitBannerBlocks } from '@rocket.chat/fuselage-ui-kit'; -// import React, { Context, FC, useMemo } from 'react'; import type { UiKitPayload, UIKitActionEvent } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -// import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; -// import { useEndpoint } from '@rocket.chat/ui-contexts'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; -const useUIKitHandleAction = (state: S): ((event: UIKitActionEvent) => Promise) => - useMutableCallback(async ({ blockId, value, appId, actionId }) => { +const useUIKitHandleAction = (state: S): ((event: UIKitActionEvent) => Promise) => { + const actionManager = useUiKitActionManager(); + return useMutableCallback(async ({ blockId, value, appId, actionId }) => { if (!appId) { throw new Error('useUIKitHandleAction - invalid appId'); } - return ActionManager.triggerBlockAction({ + return actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: state.viewId || state.appId, @@ -26,5 +21,6 @@ const useUIKitHandleAction = (state: S): ((event: UIKitA blockId, }); }); +}; export { useUIKitHandleAction }; diff --git a/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx b/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx index 85b5c24eb000..672e1b311b5d 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx @@ -1,33 +1,27 @@ import type { UIKitInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; -/* eslint-disable new-cap */ -// import { Banner, Icon } from '@rocket.chat/fuselage'; -// import { kitContext, UiKitBanner as renderUiKitBannerBlocks } from '@rocket.chat/fuselage-ui-kit'; -// import React, { Context, FC, useMemo } from 'react'; import type { UiKitPayload } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; -// import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; -// import { useEndpoint } from '@rocket.chat/ui-contexts'; - -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; // eslint-disable-next-line @typescript-eslint/no-unused-vars const emptyFn = (_error: any, _result: UIKitInteractionType | void): void => undefined; const useUIKitHandleClose = (state: S, fn = emptyFn): (() => Promise) => { + const actionManager = useUiKitActionManager(); const dispatchToastMessage = useToastMessageDispatch(); return useMutableCallback(() => - ActionManager.triggerCancel({ - appId: state.appId, - viewId: state.viewId, - view: { - ...state, - id: state.viewId, - // state: groupStateByBlockId(values), - }, - isCleared: true, - }) + actionManager + .triggerCancel({ + appId: state.appId, + viewId: state.viewId, + view: { + ...state, + id: state.viewId, + }, + isCleared: true, + }) .then((result) => fn(undefined, result)) .catch((error) => { dispatchToastMessage({ type: 'error', message: error }); diff --git a/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx b/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx index 2ada5f661787..26b329f2ea60 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx @@ -3,9 +3,10 @@ import { isErrorType } from '@rocket.chat/core-typings'; import { useSafely } from '@rocket.chat/fuselage-hooks'; import { useEffect, useState } from 'react'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; const useUIKitStateManager = (initialState: S): S => { + const actionManager = useUiKitActionManager(); const [state, setState] = useSafely(useState(initialState)); const { viewId } = state; @@ -22,10 +23,10 @@ const useUIKitStateManager = (initialState: S): S => { setState(rest as any); }; - ActionManager.on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return (): void => { - ActionManager.off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [setState, viewId]); diff --git a/apps/meteor/client/components/ActionManagerBusyState.tsx b/apps/meteor/client/components/ActionManagerBusyState.tsx new file mode 100644 index 000000000000..0374254a7de9 --- /dev/null +++ b/apps/meteor/client/components/ActionManagerBusyState.tsx @@ -0,0 +1,51 @@ +import { css } from '@rocket.chat/css-in-js'; +import { Box } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useEffect, useState } from 'react'; + +import { useUiKitActionManager } from '../hooks/useUiKitActionManager'; + +const ActionManagerBusyState = () => { + const t = useTranslation(); + const actionManager = useUiKitActionManager(); + const [busy, setBusy] = useState(false); + + useEffect(() => { + if (!actionManager) { + return; + } + + actionManager.on('busy', ({ busy }: { busy: boolean }) => setBusy(busy)); + + return () => { + actionManager.off('busy'); + }; + }, [actionManager]); + + if (busy) { + return ( + + {t('Loading')} + + ); + } + + return null; +}; + +export default ActionManagerBusyState; diff --git a/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx b/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx index 50d53da351bc..38aadf0a840b 100644 --- a/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx +++ b/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx @@ -17,7 +17,7 @@ type AutoCompleteDepartmentMultipleProps = { }; const AutoCompleteDepartmentMultiple = ({ - value, + value = [], onlyMyDepartments = false, showArchived = false, enabled = false, @@ -37,6 +37,11 @@ const AutoCompleteDepartmentMultiple = ({ const { phase: departmentsPhase, items: departmentsItems, itemCount: departmentsTotal } = useRecordList(departmentsList); + const departmentOptions = useMemo(() => { + const pending = value.filter(({ value }) => !departmentsItems.find((dep) => dep.value === value)) || []; + return [...departmentsItems, ...pending]; + }, [departmentsItems, value]); + return ( ( - + {fn()} ), diff --git a/apps/meteor/client/components/BurgerMenu/BurgerMenuButton.tsx b/apps/meteor/client/components/BurgerMenu/BurgerMenuButton.tsx index d4cc98a0d1fd..1ebe31439c1e 100644 --- a/apps/meteor/client/components/BurgerMenu/BurgerMenuButton.tsx +++ b/apps/meteor/client/components/BurgerMenu/BurgerMenuButton.tsx @@ -23,7 +23,7 @@ const BurgerMenuButton = ({ open, badge, onClick }: BurgerMenuButtonProps): Reac aria-label={open ? t('Close_menu') : t('Open_menu')} type='button' position='relative' - marginInlineEnd='x8' + marginInlineEnd={8} className={css` cursor: pointer; `} diff --git a/apps/meteor/client/components/BurgerMenu/Wrapper.tsx b/apps/meteor/client/components/BurgerMenu/Wrapper.tsx index 135e24cc7cec..279536559aaa 100644 --- a/apps/meteor/client/components/BurgerMenu/Wrapper.tsx +++ b/apps/meteor/client/components/BurgerMenu/Wrapper.tsx @@ -9,8 +9,8 @@ const Wrapper = ({ children }: { children: ReactNode }): ReactElement => ( flexDirection='column' alignItems='center' justifyContent='space-between' - paddingBlock='x4' - paddingInline='x2' + paddingBlock={4} + paddingInline={2} verticalAlign='middle' children={children} height='x24' diff --git a/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx b/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx index c18c0d750026..0865a1c26ae9 100644 --- a/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx +++ b/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx @@ -63,12 +63,12 @@ const ConfirmOwnerChangeModal: FC = ({ {contentTitle} {changeOwnerRooms && ( - + {changeOwnerRooms} )} {removedRooms && ( - + {removedRooms} )} diff --git a/apps/meteor/client/components/Contextualbar/ContextualbarClose.tsx b/apps/meteor/client/components/Contextualbar/ContextualbarClose.tsx index 18e83c50c628..6c0fbd5c8ebe 100644 --- a/apps/meteor/client/components/Contextualbar/ContextualbarClose.tsx +++ b/apps/meteor/client/components/Contextualbar/ContextualbarClose.tsx @@ -7,7 +7,7 @@ type ContextualbarCloseProps = Partial { const t = useTranslation(); - return ; + return ; }; export default memo(ContextualbarClose); diff --git a/apps/meteor/client/components/Contextualbar/ContextualbarScrollableContent.tsx b/apps/meteor/client/components/Contextualbar/ContextualbarScrollableContent.tsx index ade28661e87b..6be178f86f77 100644 --- a/apps/meteor/client/components/Contextualbar/ContextualbarScrollableContent.tsx +++ b/apps/meteor/client/components/Contextualbar/ContextualbarScrollableContent.tsx @@ -7,8 +7,8 @@ import Page from '../Page'; const ContextualbarScrollableContent = forwardRef>( function ContextualbarScrollableContent({ children, ...props }, ref) { return ( - - {children} + + {children} ); }, diff --git a/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx b/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx index 88de1d35051f..0a2717a65552 100644 --- a/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx +++ b/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx @@ -23,7 +23,7 @@ const DefaultParentRoomField = ({ defaultParentRoom }: { defaultParentRoom: stri } if (!value || !value.room) { - return {t('Error')}; + return {t('Error')}; } return ( diff --git a/apps/meteor/client/components/FilterByText.tsx b/apps/meteor/client/components/FilterByText.tsx index c97e98062fea..66cc6f2b4408 100644 --- a/apps/meteor/client/components/FilterByText.tsx +++ b/apps/meteor/client/components/FilterByText.tsx @@ -40,8 +40,8 @@ const FilterByText = ({ placeholder, onChange: setFilter, inputRef, children, au }, []); return ( - - + + {isFilterByTextPropsWithButton(props) ? ( - ) : ( diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index e9a0f39ddb5f..76868f84e3af 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -2,7 +2,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import type { ChannelMention, UserMention } from '@rocket.chat/gazzodown'; import { MarkupInteractionContext } from '@rocket.chat/gazzodown'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { useLayout, useRouter, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouter, useSetting, useUserPreference, useUserId } from '@rocket.chat/ui-contexts'; import type { UIEvent } from 'react'; import React, { useCallback, memo, useMemo } from 'react'; @@ -47,6 +47,9 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const convertAsciiToEmoji = useUserPreference('convertAsciiEmoji', true); const useEmoji = Boolean(useUserPreference('useEmojis')); + const useRealName = Boolean(useSetting('UI_Use_Real_Name')); + const ownUserId = useUserId(); + const showMentionSymbol = Boolean(useUserPreference('mentionsWithSymbol')); const chat = useChat(); @@ -80,7 +83,7 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const goToRoom = useGoToRoom(); - const { isEmbedded } = useLayout(); + const { isEmbedded, isMobile } = useLayout(); const resolveChannelMention = useCallback((mention: string) => channels?.find(({ name }) => name === mention), [channels]); @@ -117,6 +120,10 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe onChannelMentionClick, convertAsciiToEmoji, useEmoji, + useRealName, + isMobile, + ownUserId, + showMentionSymbol, }} > {children} diff --git a/apps/meteor/client/components/GenericModal/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx index 09a9598dafc2..8ae487642b92 100644 --- a/apps/meteor/client/components/GenericModal/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -1,5 +1,6 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { Button, Modal } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; @@ -15,14 +16,14 @@ type GenericModalProps = RequiredModalProps & { cancelText?: ReactNode; confirmText?: ReactNode; title?: string | ReactElement; - icon?: ComponentProps['name'] | ReactElement | null; + icon?: IconName | ReactElement | null; confirmDisabled?: boolean; tagline?: ReactNode; onCancel?: () => Promise | void; onClose?: () => Promise | void; } & Omit, 'title'>; -const iconMap: Record['name']> = { +const iconMap: Record = { danger: 'modal-warning', warning: 'modal-warning', info: 'info', @@ -73,16 +74,17 @@ const GenericModal: FC = ({ ...props }) => { const t = useTranslation(); + const genericModalId = useUniqueId(); return ( - + {renderIcon(icon, variant)} {tagline && {tagline}} - {title ?? t('Are_you_sure')} + {title ?? t('Are_you_sure')} - + {children} diff --git a/apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx b/apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx index 6d344a8a9674..9d3d754d17d4 100644 --- a/apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx +++ b/apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx @@ -49,7 +49,7 @@ function withDoNotAskAgain( {...props} dontAskAgain={ - + } diff --git a/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx b/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx index f9d0803a98d6..3fcfe2b0e0ac 100644 --- a/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx +++ b/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx @@ -1,31 +1,48 @@ -import type { Icon } from '@rocket.chat/fuselage'; -import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import { Box, States, StatesIcon, StatesLink, StatesTitle, StatesSubtitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; import React from 'react'; +type LinkProps = { linkText: string; linkHref: string } | { linkText?: never; linkHref?: never }; +type ButtonProps = { buttonTitle: string; buttonAction: () => void } | { buttonTitle?: never; buttonAction?: never }; + type GenericNoResultsProps = { - icon?: ComponentProps['name']; + icon?: IconName; title?: string; description?: string; buttonTitle?: string; - buttonAction?: () => void; -}; +} & LinkProps & + ButtonProps; -const GenericNoResults = ({ icon = 'magnifier', title, description, buttonTitle, buttonAction }: GenericNoResultsProps) => { +const GenericNoResults = ({ + icon = 'magnifier', + title, + description, + buttonTitle, + buttonAction, + linkHref, + linkText, +}: GenericNoResultsProps) => { const t = useTranslation(); return ( - - - {title || t('No_results_found')} - {description && {description}} - {buttonTitle && buttonAction && ( - - {buttonTitle} - - )} - + + + + {title || t('No_results_found')} + {description && {description}} + {buttonTitle && buttonAction && ( + + {buttonTitle} + + )} + {linkText && linkHref && ( + + {linkText} + + )} + + ); }; diff --git a/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsage.tsx b/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsage.tsx index ec81d15d640b..c226139b9489 100644 --- a/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsage.tsx +++ b/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsage.tsx @@ -21,7 +21,7 @@ const GenericResourceUsage = ({ variant?: 'warning' | 'danger' | 'success'; }) => { return ( - + {title} {subTitle && {subTitle}} diff --git a/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsageSkeleton.tsx b/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsageSkeleton.tsx index 9224fcd634de..2820379e7469 100644 --- a/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsageSkeleton.tsx +++ b/apps/meteor/client/components/GenericResourceUsage/GenericResourceUsageSkeleton.tsx @@ -3,7 +3,7 @@ import React from 'react'; const GenericResourceUsageSkeleton = ({ title, ...props }: { title?: string }) => { return ( - + {title ? {title} : } diff --git a/apps/meteor/client/components/GenericTable/GenericTable.stories.tsx b/apps/meteor/client/components/GenericTable/GenericTable.stories.tsx index f563999b0722..2fe2a834cec3 100644 --- a/apps/meteor/client/components/GenericTable/GenericTable.stories.tsx +++ b/apps/meteor/client/components/GenericTable/GenericTable.stories.tsx @@ -40,7 +40,7 @@ const results = Array.from({ length: 10 }, (_, i) => ({ const filter = ( <> - + } /> diff --git a/apps/meteor/client/components/GenericTable/GenericTable.tsx b/apps/meteor/client/components/GenericTable/GenericTable.tsx index ae33cce07a3a..00e7ed4f7cba 100644 --- a/apps/meteor/client/components/GenericTable/GenericTable.tsx +++ b/apps/meteor/client/components/GenericTable/GenericTable.tsx @@ -11,7 +11,7 @@ type GenericTableProps = { export const GenericTable = forwardRef(function GenericTable({ fixed = true, children, ...props }, ref) { return ( - + {/* TODO: Fix fuselage */} diff --git a/apps/meteor/client/components/GenericTable/GenericTableLoadingRow.tsx b/apps/meteor/client/components/GenericTable/GenericTableLoadingRow.tsx index 5c539e732b3a..d7cd06e6a286 100644 --- a/apps/meteor/client/components/GenericTable/GenericTableLoadingRow.tsx +++ b/apps/meteor/client/components/GenericTable/GenericTableLoadingRow.tsx @@ -7,7 +7,7 @@ export const GenericTableLoadingRow = ({ cols }: { cols: number }): ReactElement - + diff --git a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx new file mode 100644 index 000000000000..ec9f852c8a8b --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx @@ -0,0 +1,84 @@ +import { Box, Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactNode, ReactElement, ComponentProps } from 'react'; +import React from 'react'; + +type GenericUpsellModalProps = { + children?: ReactNode; + tagline?: ReactNode; + cancelText?: ReactNode; + confirmText?: ReactNode; + title: string | ReactElement; + subtitle?: string | ReactElement; + description?: string | ReactElement; + icon?: IconName; + img: ComponentProps['src']; + onCancel?: () => void; + onClose: () => void; + onConfirm?: () => void; + annotation?: ReactNode; +} & ComponentProps; + +const GenericUpsellModal = ({ + tagline, + title, + subtitle, + img, + cancelText, + confirmText, + icon, + description, + onCancel, + onClose, + onConfirm, + annotation, + ...props +}: GenericUpsellModalProps) => { + const t = useTranslation(); + + return ( + + + {icon && } + + {tagline ?? t('Enterprise_capability')} + {title} + + + + + + {subtitle && ( + + {subtitle} + + )} + {description && ( + + {description} + + )} + + + {annotation && {annotation}} + {(onCancel || onConfirm) && ( + + {onCancel && ( + + )} + {onConfirm && ( + + )} + + )} + + + ); +}; + +export default GenericUpsellModal; diff --git a/apps/meteor/client/components/GenericUpsellModal/hooks/index.ts b/apps/meteor/client/components/GenericUpsellModal/hooks/index.ts new file mode 100644 index 000000000000..f7af26c76c48 --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/hooks/index.ts @@ -0,0 +1 @@ +export * from './useUpsellActions'; diff --git a/apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts b/apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts new file mode 100644 index 000000000000..0c3b111630ca --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/hooks/useUpsellActions.ts @@ -0,0 +1,29 @@ +import { useRouter, useSetModal, useSetting } from '@rocket.chat/ui-contexts'; +import { useCallback } from 'react'; + +import { useExternalLink } from '../../../hooks/useExternalLink'; +import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; + +const TALK_TO_SALES_URL = 'https://go.rocket.chat/i/contact-sales'; + +export const useUpsellActions = (hasLicenseModule = false) => { + const router = useRouter(); + const setModal = useSetModal(); + const handleOpenLink = useExternalLink(); + const cloudWorkspaceHadTrial = useSetting('Cloud_Workspace_Had_Trial'); + + const { data } = useIsEnterprise(); + const shouldShowUpsell = !data?.isEnterprise || !hasLicenseModule; + + const handleGoFullyFeatured = useCallback(() => { + router.navigate('/admin/upgrade/go-fully-featured-registered'); + setModal(null); + }, [router, setModal]); + + const handleTalkToSales = useCallback(() => { + handleOpenLink(TALK_TO_SALES_URL); + setModal(null); + }, [handleOpenLink, setModal]); + + return { shouldShowUpsell, cloudWorkspaceHadTrial, handleGoFullyFeatured, handleTalkToSales }; +}; diff --git a/apps/meteor/client/components/GenericUpsellModal/index.ts b/apps/meteor/client/components/GenericUpsellModal/index.ts new file mode 100644 index 000000000000..0c93e070adf2 --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/index.ts @@ -0,0 +1 @@ +export { default } from './GenericUpsellModal'; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx b/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx index 9d29007f9b90..4e8e44b1f932 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx @@ -24,7 +24,7 @@ export const Default: ComponentStory = () => ( - + diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx index d185c6ac5e68..bd2c7d100b53 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx @@ -1,9 +1,10 @@ -import { Icon, Button } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; type InfoPanelActionProps = Omit, 'label'> & { - icon?: ComponentProps['name']; + icon?: IconName; label: ReactNode; }; @@ -12,9 +13,9 @@ const InfoPanelAction = ({ label, icon, ...props }: InfoPanelActionProps): React title={typeof label === 'string' ? label : undefined} aria-label={typeof label === 'string' ? label : undefined} {...props} - mi='x4' + mi={4} + icon={icon} > - {icon && } {label} ); diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelField.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelField.tsx index 0ec4f7030b69..982e6ab8e25d 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelField.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelField.tsx @@ -2,6 +2,6 @@ import { Box } from '@rocket.chat/fuselage'; import type { FC } from 'react'; import React from 'react'; -const InfoPanelField: FC = ({ children }) => {children}; +const InfoPanelField: FC = ({ children }) => {children}; export default InfoPanelField; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelLabel.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelLabel.tsx index 5bd09c561919..77450ea0e9ea 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelLabel.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelLabel.tsx @@ -2,6 +2,6 @@ import { Box } from '@rocket.chat/fuselage'; import type { ComponentProps, FC } from 'react'; import React from 'react'; -const InfoPanelLabel: FC> = (props) => ; +const InfoPanelLabel: FC> = (props) => ; export default InfoPanelLabel; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelSection.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelSection.tsx index 78b0a7b02027..7db13dad751f 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelSection.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelSection.tsx @@ -2,6 +2,6 @@ import { Box } from '@rocket.chat/fuselage'; import type { ComponentProps, FC } from 'react'; import React from 'react'; -const InfoPanelSection: FC> = (props) => ; +const InfoPanelSection: FC> = (props) => ; export default InfoPanelSection; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelText.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelText.tsx index 50e4c4d82460..7b82d0d02aa0 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelText.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelText.tsx @@ -8,7 +8,7 @@ const wordBreak = css` `; const InfoPanelText: FC> = (props) => ( - + ); export default InfoPanelText; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx index 298a01bd2045..7ea4de6d9867 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx @@ -1,5 +1,6 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, FC, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { FC, ReactNode } from 'react'; import React from 'react'; type InfoPanelTitleProps = { @@ -7,12 +8,12 @@ type InfoPanelTitleProps = { icon: ReactNode; }; -const isValidIcon = (icon: ReactNode): icon is ComponentProps['name'] => typeof icon === 'string'; +const isValidIcon = (icon: ReactNode): icon is IconName => typeof icon === 'string'; const InfoPanelTitle: FC = ({ title, icon }) => ( {isValidIcon(icon) ? : icon} - + {title} diff --git a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx index 9e261c88af77..27202afa496c 100644 --- a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx +++ b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx @@ -13,7 +13,7 @@ type RetentionPolicyCalloutProps = { const RetentionPolicyCallout: FC = ({ filesOnlyDefault, excludePinnedDefault, maxAgeDefault }) => { const t = useTranslation(); - const time = useFormattedRelativeTime(maxAgeDefault * 1000 * 60 * 60 * 24); + const time = useFormattedRelativeTime(maxAgeDefault); return ( diff --git a/apps/meteor/client/components/ListItem.stories.tsx b/apps/meteor/client/components/ListItem.stories.tsx index 712e9f46ea66..d2ff597f46dc 100644 --- a/apps/meteor/client/components/ListItem.stories.tsx +++ b/apps/meteor/client/components/ListItem.stories.tsx @@ -19,7 +19,7 @@ export default { export const ListWithIcon: ComponentStory = () => { return ( - + Title @@ -32,7 +32,7 @@ export const ListWithIcon: ComponentStory = () => { export const NoIcon: ComponentStory = () => { return ( - + Title @@ -46,7 +46,7 @@ export const NoIcon: ComponentStory = () => { export const MixedWithGap: ComponentStory = () => { return ( - + Title @@ -67,7 +67,7 @@ MixedWithGap.parameters = { export const MixedWithoutGap: ComponentStory = () => { return ( - + Title diff --git a/apps/meteor/client/components/Navbar/Navbar.stories.tsx b/apps/meteor/client/components/Navbar/Navbar.stories.tsx new file mode 100644 index 000000000000..a08e88a784a7 --- /dev/null +++ b/apps/meteor/client/components/Navbar/Navbar.stories.tsx @@ -0,0 +1,13 @@ +import { Box } from '@rocket.chat/fuselage'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import React from 'react'; + +import { Navbar } from './Navbar'; + +export default { + title: 'Components/Navbar', + component: Box, + decorators: [(story) => {story()}], +} as ComponentMeta; + +export const Default: ComponentStory = (_args) => ; diff --git a/apps/meteor/client/components/Navbar/Navbar.tsx b/apps/meteor/client/components/Navbar/Navbar.tsx new file mode 100644 index 000000000000..2963f06ae494 --- /dev/null +++ b/apps/meteor/client/components/Navbar/Navbar.tsx @@ -0,0 +1,13 @@ +import { Box, ButtonGroup } from '@rocket.chat/fuselage'; +import type { FC } from 'react'; +import React from 'react'; + +export const Navbar: FC = ({ children }) => { + return ( + + + {children} + + + ); +}; diff --git a/apps/meteor/client/components/Navbar/NavbarAction.tsx b/apps/meteor/client/components/Navbar/NavbarAction.tsx new file mode 100644 index 000000000000..470f754d861a --- /dev/null +++ b/apps/meteor/client/components/Navbar/NavbarAction.tsx @@ -0,0 +1,10 @@ +import type { FC } from 'react'; +import React from 'react'; + +export const NavbarAction: FC = ({ children, ...props }) => { + return ( +
  • + {children} +
  • + ); +}; diff --git a/apps/meteor/client/components/Navbar/NavbarBadge.tsx b/apps/meteor/client/components/Navbar/NavbarBadge.tsx new file mode 100644 index 000000000000..2ab490ca0c77 --- /dev/null +++ b/apps/meteor/client/components/Navbar/NavbarBadge.tsx @@ -0,0 +1,11 @@ +import { Badge } from '@rocket.chat/fuselage'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +export const NavbarBadge = (props: Omit, 'is'>) => { + return ( +
    + +
    + ); +}; diff --git a/apps/meteor/client/components/Navbar/index.ts b/apps/meteor/client/components/Navbar/index.ts new file mode 100644 index 000000000000..7e752b2a8844 --- /dev/null +++ b/apps/meteor/client/components/Navbar/index.ts @@ -0,0 +1,3 @@ +export * from './Navbar'; +export * from './NavbarAction'; +export * from './NavbarBadge'; diff --git a/apps/meteor/client/components/NotFoundState.tsx b/apps/meteor/client/components/NotFoundState.tsx index 421fa1a9ca87..9ed42fc14366 100644 --- a/apps/meteor/client/components/NotFoundState.tsx +++ b/apps/meteor/client/components/NotFoundState.tsx @@ -22,7 +22,7 @@ const NotFoundState = ({ title, subtitle }: NotFoundProps): ReactElement => { {title} {subtitle} - + {t('Homepage')} diff --git a/apps/meteor/client/components/Omnichannel/Skeleton.tsx b/apps/meteor/client/components/Omnichannel/Skeleton.tsx index 0afb367fc892..ebbd07d361e7 100644 --- a/apps/meteor/client/components/Omnichannel/Skeleton.tsx +++ b/apps/meteor/client/components/Omnichannel/Skeleton.tsx @@ -3,8 +3,8 @@ import type { FC } from 'react'; import React from 'react'; export const FormSkeleton: FC = (props) => ( - - - + + + ); diff --git a/apps/meteor/client/components/Omnichannel/Tags.tsx b/apps/meteor/client/components/Omnichannel/Tags.tsx index c0edcd96164b..39564ca7f89f 100644 --- a/apps/meteor/client/components/Omnichannel/Tags.tsx +++ b/apps/meteor/client/components/Omnichannel/Tags.tsx @@ -71,7 +71,7 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) return ( <> - + {t('Tags')} @@ -96,7 +96,7 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) flexGrow={1} placeholder={t('Enter_a_tag')} /> - @@ -106,7 +106,7 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) {customTags.length > 0 && ( {customTags?.map((tag, i) => ( - removeTag(tag)} mie='x8'> + removeTag(tag)} mie={8}> {tag} ))} diff --git a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts index 4bd85be40342..ce5704b66482 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts @@ -1,6 +1,8 @@ import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; +import { useOmnichannel } from '../../../hooks/omnichannel/useOmnichannel'; + type Props = { department?: string; text?: string; @@ -9,13 +11,19 @@ type Props = { export const useLivechatTags = (options: Props) => { const getTags = useEndpoint('GET', '/v1/livechat/tags'); + const { isEnterprise } = useOmnichannel(); const { department, text, viewAll } = options; - return useQuery(['/v1/livechat/tags', text, department], () => - getTags({ - text: text || '', - ...(department && { department }), - viewAll: viewAll ? 'true' : 'false', - }), + return useQuery( + ['/v1/livechat/tags', text, department], + () => + getTags({ + text: text || '', + ...(department && { department }), + viewAll: viewAll ? 'true' : 'false', + }), + { + enabled: isEnterprise, + }, ); }; diff --git a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx index c8307e2045f9..17cbc094160b 100644 --- a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx @@ -1,5 +1,5 @@ import type { ILivechatDepartment } from '@rocket.chat/core-typings'; -import { Field, Button, TextInput, Modal, Box, CheckBox, Divider, EmailInput } from '@rocket.chat/fuselage'; +import { Field, FieldGroup, Button, TextInput, Modal, Box, CheckBox, Divider, EmailInput } from '@rocket.chat/fuselage'; import { usePermission, useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useCallback, useState, useEffect, useMemo } from 'react'; @@ -68,7 +68,7 @@ const CloseChatModal = ({ }; const requestData = transcriptEmail && visitorEmail ? { email: visitorEmail, subject } : undefined; - if (!comment && commentRequired) { + if (!comment?.trim() && commentRequired) { setError('comment', { type: 'custom', message: t('The_field_is_required', t('Comment')) }); } @@ -89,7 +89,7 @@ const CloseChatModal = ({ const cannotSubmit = useMemo(() => { const cannotSendTag = (tagRequired && !tags?.length) || errors.tags; - const cannotSendComment = (commentRequired && !comment) || errors.comment; + const cannotSendComment = (commentRequired && !comment?.trim()) || errors.comment; const cannotSendTranscriptEmail = transcriptEmail && (!visitorEmail || !subject); return Boolean(cannotSendTag || cannotSendComment || cannotSendTranscriptEmail); @@ -132,91 +132,93 @@ const CloseChatModal = ({ {t('Close_room_description')} - - {t('Comment')} - - - - {errors.comment?.message} - - - - {errors.tags?.message} - - {canSendTranscript && ( - <> - - - {t('Chat_transcript')} - - {canSendTranscriptPDF && ( - - - - - {t('Omnichannel_transcript_pdf')} - - + + + {t('Comment')} + + + + {errors.comment?.message} + + + + {errors.tags?.message} + + {canSendTranscript && ( + <> + + + {t('Chat_transcript')} - )} - {canSendTranscriptEmail && ( - <> - + {canSendTranscriptPDF && ( + - - - {t('Omnichannel_transcript_email')} + + + {t('Omnichannel_transcript_pdf')} - {transcriptEmail && ( - <> - - {t('Contact_email')} - - - - - - {t('Subject')} - - - - {errors.subject?.message} - - - )} - - )} - - - {canSendTranscriptPDF && canSendTranscriptEmail - ? t('These_options_affect_this_conversation_only_To_set_default_selections_go_to_My_Account_Omnichannel') - : t('This_option_affect_this_conversation_only_To_set_default_selection_go_to_My_Account_Omnichannel')} - - - - )} + )} + {canSendTranscriptEmail && ( + <> + + + + + {t('Omnichannel_transcript_email')} + + + + {transcriptEmail && ( + <> + + {t('Contact_email')} + + + + + + {t('Subject')} + + + + {errors.subject?.message} + + + )} + + )} + + + {canSendTranscriptPDF && canSendTranscriptEmail + ? t('These_options_affect_this_conversation_only_To_set_default_selections_go_to_My_Account_Omnichannel') + : t('This_option_affect_this_conversation_only_To_set_default_selection_go_to_My_Account_Omnichannel')} + + + + )} + diff --git a/apps/meteor/client/components/Omnichannel/modals/EnterpriseDepartmentsModal.tsx b/apps/meteor/client/components/Omnichannel/modals/EnterpriseDepartmentsModal.tsx index 8ef394b43149..8187776a4192 100644 --- a/apps/meteor/client/components/Omnichannel/modals/EnterpriseDepartmentsModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/EnterpriseDepartmentsModal.tsx @@ -44,7 +44,7 @@ const EnterpriseDepartmentsModal = ({ closeModal }: { closeModal: () => void }): - + {t('Enterprise_Departments_title')} {isTypeUpgradeYourPlan ? t('Enterprise_Departments_description_free_trial') : t('Enterprise_Departments_description_upgrade')} diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index 9ff01a4b7d6c..732556335c7f 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -1,5 +1,5 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { Field, Button, TextAreaInput, Modal, Box, PaginatedSelectFiltered, Divider } from '@rocket.chat/fuselage'; +import { Field, FieldGroup, Button, TextAreaInput, Modal, Box, PaginatedSelectFiltered, Divider } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -41,7 +41,6 @@ const ForwardChatModal = ({ useMemo(() => ({ filter: debouncedDepartmentsFilter as string, enabled: true }), [debouncedDepartmentsFilter]), ); const { phase: departmentsPhase, items: departments, itemCount: departmentsTotal } = useRecordList(departmentsList); - const hasDepartments = useMemo(() => departments && departments.length > 0, [departments]); const _id = { $ne: room.servedBy?._id }; const conditions = { @@ -67,7 +66,7 @@ const ForwardChatModal = ({ const endReached = useCallback( (start) => { - if (departmentsPhase === AsyncStatePhase.LOADING) { + if (departmentsPhase !== AsyncStatePhase.LOADING) { loadMoreDepartments(start, Math.min(50, departmentsTotal)); } }, @@ -101,10 +100,10 @@ const ForwardChatModal = ({ - - {t('Forward_to_department')} - - { + + + {t('Forward_to_department')} + - } - - - - - {t('Forward_to_user')} - - { - setValue('username', value); - }} - value={getValues().username} - /> - - - - - {t('Leave_a_comment')}{' '} - - ({t('Optional')}) - - - - - - + + + + + {t('Forward_to_user')} + + { + setValue('username', value); + }} + value={getValues().username} + /> + + + + + {t('Leave_a_comment')}{' '} + + ({t('Optional')}) + + + + + + + diff --git a/apps/meteor/client/components/Omnichannel/modals/TranscriptModal.tsx b/apps/meteor/client/components/Omnichannel/modals/TranscriptModal.tsx index c5bd65d82421..e879caf38032 100644 --- a/apps/meteor/client/components/Omnichannel/modals/TranscriptModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/TranscriptModal.tsx @@ -1,5 +1,5 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { Field, Button, TextInput, Modal, Box } from '@rocket.chat/fuselage'; +import { Field, Button, TextInput, Modal, Box, FieldGroup } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { useCallback, useEffect } from 'react'; @@ -68,7 +68,7 @@ const TranscriptModal: FC = ({ const canSubmit = isValid && Boolean(watch('subject')); return ( - } {...props}> + } {...props}> {t('Transcript')} @@ -76,30 +76,32 @@ const TranscriptModal: FC = ({ {!!transcriptRequest &&

    {t('Livechat_transcript_already_requested_warning')}

    } - - {t('Email')}* - - - - {errors.email?.message} - - - {t('Subject')}* - - - - {errors.subject?.message} - + + + {t('Email')}* + + + + {errors.email?.message} + + + {t('Subject')}* + + + + {errors.subject?.message} + +
    diff --git a/apps/meteor/client/components/Page/Page.tsx b/apps/meteor/client/components/Page/Page.tsx index f3cabcce81a6..59f0849e5333 100644 --- a/apps/meteor/client/components/Page/Page.tsx +++ b/apps/meteor/client/components/Page/Page.tsx @@ -5,7 +5,7 @@ import React, { useState } from 'react'; import PageContext from './PageContext'; type PageProps = Omit, 'backgroundColor'> & { - background?: 'light' | 'tint' | 'neutral'; + background?: 'light' | 'tint' | 'neutral' | 'room'; }; const Page = ({ background = 'light', ...props }: PageProps): ReactElement => { @@ -20,7 +20,6 @@ const Page = ({ background = 'light', ...props }: PageProps): ReactElement => { flexShrink={1} height='full' overflow='hidden' - aria-labelledby='PageHeader-title' bg={background} color='default' {...props} diff --git a/apps/meteor/client/components/Page/PageBlockWithBorder.tsx b/apps/meteor/client/components/Page/PageBlockWithBorder.tsx index f36927a06aee..8176f6734bfd 100644 --- a/apps/meteor/client/components/Page/PageBlockWithBorder.tsx +++ b/apps/meteor/client/components/Page/PageBlockWithBorder.tsx @@ -10,7 +10,7 @@ const PageBlockWithBorder = forwardRef>( return ( >(function PageContent(props, ref) { - return ; + return ; }); export default PageContent; diff --git a/apps/meteor/client/components/Page/PageFooter.tsx b/apps/meteor/client/components/Page/PageFooter.tsx new file mode 100644 index 000000000000..1cae326589a4 --- /dev/null +++ b/apps/meteor/client/components/Page/PageFooter.tsx @@ -0,0 +1,29 @@ +import { AnimatedVisibility, Box } from '@rocket.chat/fuselage'; +import type { FC, ComponentProps } from 'react'; +import React from 'react'; + +type PageFooterProps = { isDirty: boolean } & ComponentProps; + +const PageFooter: FC = ({ children, isDirty, ...props }) => { + return ( + + + + {children} + + + + ); +}; + +export default PageFooter; diff --git a/apps/meteor/client/components/Page/PageHeader.tsx b/apps/meteor/client/components/Page/PageHeader.tsx index c6fb9846ebc4..6725c2da6f5c 100644 --- a/apps/meteor/client/components/Page/PageHeader.tsx +++ b/apps/meteor/client/components/Page/PageHeader.tsx @@ -1,4 +1,5 @@ import { Box, IconButton } from '@rocket.chat/fuselage'; +import { useAutoFocus } from '@rocket.chat/fuselage-hooks'; import { HeaderToolbox } from '@rocket.chat/ui-client'; import { useLayout, useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ComponentProps, ReactNode } from 'react'; @@ -17,32 +18,27 @@ const PageHeader: FC = ({ children = undefined, title, onClickB const t = useTranslation(); const [border] = useContext(PageContext); const { isMobile } = useLayout(); + const headerAutoFocus = useAutoFocus(); return ( - + {isMobile && ( )} - {onClickBack && } - + {onClickBack && } + {title} {children} diff --git a/apps/meteor/client/components/Page/index.ts b/apps/meteor/client/components/Page/index.ts index 9f857f22f2cc..9aad9332937a 100644 --- a/apps/meteor/client/components/Page/index.ts +++ b/apps/meteor/client/components/Page/index.ts @@ -1,5 +1,6 @@ import Page from './Page'; import PageContent from './PageContent'; +import PageFooter from './PageFooter'; import PageHeader from './PageHeader'; import PageScrollableContent from './PageScrollableContent'; import PageScrollableContentWithShadow from './PageScrollableContentWithShadow'; @@ -9,4 +10,5 @@ export default Object.assign(Page, { Content: PageContent, ScrollableContent: PageScrollableContent, ScrollableContentWithShadow: PageScrollableContentWithShadow, + Footer: PageFooter, }); diff --git a/apps/meteor/client/components/PageSkeleton.tsx b/apps/meteor/client/components/PageSkeleton.tsx index 3cf519088eea..2c3c588d6c4c 100644 --- a/apps/meteor/client/components/PageSkeleton.tsx +++ b/apps/meteor/client/components/PageSkeleton.tsx @@ -8,7 +8,7 @@ const PageSkeleton = (): ReactElement => ( }> - + - )} - {onConfirm && ( - - )} - - -
    - ); -}; - -export default UpsellModal; diff --git a/apps/meteor/client/components/UrlChangeModal.tsx b/apps/meteor/client/components/UrlChangeModal.tsx index 1418d04c5b33..13d0523c92aa 100644 --- a/apps/meteor/client/components/UrlChangeModal.tsx +++ b/apps/meteor/client/components/UrlChangeModal.tsx @@ -18,7 +18,7 @@ const UrlChangeModal = ({ onConfirm, siteUrl, currentUrl, onClose }: UrlChangeMo ( - + {label.t === 'd' ? ( ) : ( )} - + {label.name} diff --git a/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple.tsx/index.ts b/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple/index.ts similarity index 100% rename from apps/meteor/client/components/UserAndRoomAutoCompleteMultiple.tsx/index.ts rename to apps/meteor/client/components/UserAndRoomAutoCompleteMultiple/index.ts diff --git a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx index f557c5a339e4..ebe3ecc5ee72 100644 --- a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx +++ b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx @@ -39,9 +39,9 @@ const UserAutoComplete = ({ value, onChange, ...props }: UserAutoCompleteProps): setFilter={setFilter} data-qa-id='UserAutoComplete' renderSelected={({ selected: { value, label } }): ReactElement | null => ( - + - + {label} diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx index f1f079150bbe..dfda12cdd2e4 100644 --- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx +++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx @@ -32,9 +32,9 @@ const UserAutoCompleteMultiple = ({ onChange, ...props }: UserAutoCompleteMultip onChange={onChange} multiple renderSelected={({ selected: { value, label }, onRemove }): ReactElement => ( - + - + {label} diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx index 0d04d555ae2d..457593e2c5db 100644 --- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx +++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated.tsx @@ -2,7 +2,7 @@ import { MultiSelectFiltered, Icon, Box, Chip } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import type { ReactElement } from 'react'; +import type { ReactElement, AllHTMLAttributes } from 'react'; import React, { memo, useState, useCallback, useMemo } from 'react'; import UserAvatar from '../avatar/UserAvatar'; @@ -12,7 +12,7 @@ type UserAutoCompleteMultipleFederatedProps = { onChange: (value: Array) => void; value: Array; placeholder?: string; -}; +} & Omit, 'is' | 'onChange'>; type UserAutoCompleteOptionType = { name: string; @@ -93,19 +93,20 @@ const UserAutoCompleteMultipleFederated = ({ return ( void }): ReactElement => { + renderSelected={({ value, onMouseDown }: { value: string; onMouseDown: () => void }) => { const currentCachedOption = selectedCache[value] || {}; return ( - + {currentCachedOption._federated ? : } - + {currentCachedOption.name || currentCachedOption.username || value} diff --git a/apps/meteor/client/components/UserCard/UserCard.tsx b/apps/meteor/client/components/UserCard/UserCard.tsx index 84bc8672b595..83edd9e3d8cb 100644 --- a/apps/meteor/client/components/UserCard/UserCard.tsx +++ b/apps/meteor/client/components/UserCard/UserCard.tsx @@ -50,9 +50,9 @@ const UserCard = forwardRef(function UserCard( customStatus = , roles = ( <> - - - + + + ), bio = , @@ -76,29 +76,29 @@ const UserCard = forwardRef(function UserCard( ) : ( )} - + {isLoading ? ( <> - - - + + + ) : ( actions )} - - + + {isLoading ? : } {nickname && ( - + ({nickname}) )} {customStatus && ( - + {typeof customStatus === 'string' ? ( ) : ( diff --git a/apps/meteor/client/components/UserCard/UserCardAction.tsx b/apps/meteor/client/components/UserCard/UserCardAction.tsx index e17716886dac..f13e71b601b5 100644 --- a/apps/meteor/client/components/UserCard/UserCardAction.tsx +++ b/apps/meteor/client/components/UserCard/UserCardAction.tsx @@ -5,7 +5,7 @@ import React from 'react'; type UserCardActionProps = ComponentProps; const UserCardAction = ({ label, icon, ...props }: UserCardActionProps): ReactElement => ( - + ); export default UserCardAction; diff --git a/apps/meteor/client/components/UserCard/UserCardContainer.tsx b/apps/meteor/client/components/UserCard/UserCardContainer.tsx index 4d897e182ef1..c1279e4ff513 100644 --- a/apps/meteor/client/components/UserCard/UserCardContainer.tsx +++ b/apps/meteor/client/components/UserCard/UserCardContainer.tsx @@ -3,7 +3,7 @@ import type { ComponentProps } from 'react'; import React, { forwardRef } from 'react'; const UserCardContainer = forwardRef(function UserCardContainer(props: ComponentProps, ref) { - return ; + return ; }); export default UserCardContainer; diff --git a/apps/meteor/client/components/UserCard/UserCardInfo.tsx b/apps/meteor/client/components/UserCard/UserCardInfo.tsx index 1db62882c740..8e235670a3dc 100644 --- a/apps/meteor/client/components/UserCard/UserCardInfo.tsx +++ b/apps/meteor/client/components/UserCard/UserCardInfo.tsx @@ -3,7 +3,7 @@ import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; const UserCardInfo = (props: ComponentProps): ReactElement => ( - + ); export default UserCardInfo; diff --git a/apps/meteor/client/components/UserCard/UserCardRole.tsx b/apps/meteor/client/components/UserCard/UserCardRole.tsx index 2934aef8eda0..2b558af6b9b3 100644 --- a/apps/meteor/client/components/UserCard/UserCardRole.tsx +++ b/apps/meteor/client/components/UserCard/UserCardRole.tsx @@ -3,7 +3,7 @@ import type { ReactNode, ReactElement } from 'react'; import React from 'react'; const UserCardRole = ({ children }: { children: ReactNode }): ReactElement => ( - + ); diff --git a/apps/meteor/client/components/UserCard/UserCardUsername.tsx b/apps/meteor/client/components/UserCard/UserCardUsername.tsx index b72344a10e33..4aff0192d316 100644 --- a/apps/meteor/client/components/UserCard/UserCardUsername.tsx +++ b/apps/meteor/client/components/UserCard/UserCardUsername.tsx @@ -23,7 +23,7 @@ const UserCardUsername = ({ name, status = , ...props }: U {...props} > {status} - + {name} diff --git a/apps/meteor/client/components/UserInfo/UserInfo.tsx b/apps/meteor/client/components/UserInfo/UserInfo.tsx index 142f31026d6e..bdce27d028ad 100644 --- a/apps/meteor/client/components/UserInfo/UserInfo.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfo.tsx @@ -67,7 +67,7 @@ const UserInfo = ({ const userCustomFields = useUserCustomFields(customFields); return ( - + {username && ( @@ -156,7 +156,7 @@ const UserInfo = ({ {email} - + {verified ? t('Verified') : t('Not_verified')} diff --git a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx index 958606e3570e..97c64ecbede1 100644 --- a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx @@ -1,14 +1,14 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; type UserInfoActionProps = { - icon: ComponentProps['name']; + icon: IconName; } & ComponentProps; const UserInfoAction = ({ icon, label, ...props }: UserInfoActionProps): ReactElement => ( - ); diff --git a/apps/meteor/client/components/UserStatusMenu.tsx b/apps/meteor/client/components/UserStatusMenu.tsx index 72f526998616..8f2dd24d6017 100644 --- a/apps/meteor/client/components/UserStatusMenu.tsx +++ b/apps/meteor/client/components/UserStatusMenu.tsx @@ -28,7 +28,7 @@ const UserStatusMenu = ({ const options = useMemo(() => { const renderOption = (status: UserStatusType, label: string): ReactElement => ( - + {label} @@ -87,6 +87,7 @@ const UserStatusMenu = ({ onKeyUp={handleKeyUp} onKeyDown={handleKeyDown} margin={margin} + aria-label={t('User_status_menu')} > diff --git a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx index 8405677dc26a..32948eb42243 100644 --- a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx @@ -1,7 +1,7 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; -import { Box, Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -59,23 +59,21 @@ const RoomAvatarEditor = ({ disabled = false, room, roomAvatar, onChangeAvatar } `, ]} position='absolute' - m='x12' + m={12} > - + /> diff --git a/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx b/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx index 201e8d7a6413..6d6ed93d3929 100644 --- a/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx @@ -17,21 +17,15 @@ const toDataURL = (file: File, callback: (result: FileReader['result']) => void) reader.readAsDataURL(file); }; -type AvatarSuggestion = { - service: string; - url: string; -}; - type UserAvatarEditorType = { currentUsername: IUser['username']; username: IUser['username']; setAvatarObj: (obj: AvatarObject) => void; - suggestions: AvatarSuggestion[] | undefined; - disabled: boolean; + disabled?: boolean; etag: IUser['avatarETag']; }; -function UserAvatarEditor({ currentUsername, username, setAvatarObj, suggestions, disabled, etag }: UserAvatarEditorType): ReactElement { +function UserAvatarEditor({ currentUsername, username, setAvatarObj, disabled, etag }: UserAvatarEditorType): ReactElement { const t = useTranslation(); const rotateImages = useSetting('FileUpload_RotateImages'); const [avatarFromUrl, setAvatarFromUrl] = useState(''); @@ -74,7 +68,7 @@ function UserAvatarEditor({ currentUsername, username, setAvatarObj, suggestions return ( {t('Profile_picture')} - + - + - - @@ -100,17 +94,10 @@ function UserAvatarEditor({ currentUsername, username, setAvatarObj, suggestions disabled={disabled || !avatarFromUrl} title={t('Add_URL')} /> - {suggestions && ( - - )} + - + {t('Use_url_for_avatar')} () => { - setAvatarObj(suggestion); - setNewAvatarSource(suggestion.blob); - }, - [setAvatarObj, setNewAvatarSource], - ); - - return ( - - {Object.values(suggestions).map((suggestion) => ( - - ))} - - ); -} - -export default UserAvatarSuggestions; diff --git a/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx b/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx new file mode 100644 index 000000000000..04b0c92acd95 --- /dev/null +++ b/apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx @@ -0,0 +1,43 @@ +import type { AvatarObject } from '@rocket.chat/core-typings'; +import { Box, Button, Margins, Avatar } from '@rocket.chat/fuselage'; +import React, { useCallback } from 'react'; + +import { useAvatarSuggestions } from '../../../hooks/useAvatarSuggestions'; + +type UserAvatarSuggestionsProps = { + setAvatarObj: (obj: AvatarObject) => void; + setNewAvatarSource: (source: string) => void; + disabled?: boolean; +}; + +const UserAvatarSuggestions = ({ setAvatarObj, setNewAvatarSource, disabled }: UserAvatarSuggestionsProps) => { + const handleClick = useCallback( + (suggestion) => () => { + setAvatarObj(suggestion); + setNewAvatarSource(suggestion.blob); + }, + [setAvatarObj, setNewAvatarSource], + ); + + const { data } = useAvatarSuggestions(); + const suggestions = Object.values(data?.suggestions || {}); + + return ( + + {suggestions && + suggestions.length > 0 && + suggestions.map( + (suggestion) => + suggestion.blob && ( + + ), + )} + + ); +}; + +export default UserAvatarSuggestions; diff --git a/apps/meteor/client/components/dataView/Counter.tsx b/apps/meteor/client/components/dataView/Counter.tsx index 31a2ec01493c..8203a1169667 100644 --- a/apps/meteor/client/components/dataView/Counter.tsx +++ b/apps/meteor/client/components/dataView/Counter.tsx @@ -28,7 +28,7 @@ const Counter = ({ count, variation = 0, description }: CounterProps): ReactElem {variation} - + {description} diff --git a/apps/meteor/client/components/message/IgnoredContent.tsx b/apps/meteor/client/components/message/IgnoredContent.tsx index 7a155061713f..31b62639b849 100644 --- a/apps/meteor/client/components/message/IgnoredContent.tsx +++ b/apps/meteor/client/components/message/IgnoredContent.tsx @@ -18,7 +18,7 @@ const IgnoredContent = ({ onShowMessageIgnored }: IgnoredContentProps): ReactEle return ( - +

    {t('Message_Ignored')}

    diff --git a/apps/meteor/client/components/message/MessageToolboxHolder.tsx b/apps/meteor/client/components/message/MessageToolboxHolder.tsx new file mode 100644 index 000000000000..06a9fbf42b77 --- /dev/null +++ b/apps/meteor/client/components/message/MessageToolboxHolder.tsx @@ -0,0 +1,54 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import { MessageToolboxWrapper } from '@rocket.chat/fuselage'; +import { useQuery } from '@tanstack/react-query'; +import type { ReactElement } from 'react'; +import React, { Suspense, lazy, memo, useRef, useState } from 'react'; + +import type { MessageActionContext } from '../../../app/ui-utils/client/lib/MessageAction'; +import { useChat } from '../../views/room/contexts/ChatContext'; +import { useIsVisible } from '../../views/room/hooks/useIsVisible'; + +type MessageToolboxHolderProps = { + message: IMessage; + context?: MessageActionContext; +}; + +const MessageToolbox = lazy(() => import('./toolbox/MessageToolbox')); + +const MessageToolboxHolder = ({ message, context }: MessageToolboxHolderProps): ReactElement => { + const ref = useRef(null); + + const [isVisible] = useIsVisible(ref); + const [kebabOpen, setKebabOpen] = useState(false); + + const showToolbox = isVisible || kebabOpen; + + const chat = useChat(); + + const depsQueryResult = useQuery(['toolbox', message._id, context], async () => { + const room = await chat?.data.findRoom(); + const subscription = await chat?.data.findSubscription(); + return { + room, + subscription, + }; + }); + + return ( + + {showToolbox && depsQueryResult.isSuccess && depsQueryResult.data.room && ( + + + + )} + + ); +}; + +export default memo(MessageToolboxHolder); diff --git a/apps/meteor/client/components/message/ToolboxHolder.tsx b/apps/meteor/client/components/message/ToolboxHolder.tsx deleted file mode 100644 index 0b5199d29eb2..000000000000 --- a/apps/meteor/client/components/message/ToolboxHolder.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { IMessage } from '@rocket.chat/core-typings'; -import { MessageToolboxWrapper } from '@rocket.chat/fuselage'; -import { useQuery } from '@tanstack/react-query'; -import type { ReactElement } from 'react'; -import React, { memo, useRef } from 'react'; - -import type { MessageActionContext } from '../../../app/ui-utils/client/lib/MessageAction'; -import { useChat } from '../../views/room/contexts/ChatContext'; -import { useIsVisible } from '../../views/room/hooks/useIsVisible'; -import Toolbox from './toolbox/Toolbox'; - -type ToolboxHolderProps = { - message: IMessage; - context?: MessageActionContext; -}; - -const ToolboxHolder = ({ message, context }: ToolboxHolderProps): ReactElement => { - const ref = useRef(null); - - const [visible] = useIsVisible(ref); - - const chat = useChat(); - - const depsQueryResult = useQuery(['toolbox', message._id, context], async () => { - const room = await chat?.data.findRoom(); - const subscription = await chat?.data.findSubscription(); - return { - room, - subscription, - }; - }); - - return ( - - {visible && depsQueryResult.isSuccess && depsQueryResult.data.room && ( - - )} - - ); -}; - -export default memo(ToolboxHolder); diff --git a/apps/meteor/client/components/message/content/MessageActions.tsx b/apps/meteor/client/components/message/content/MessageActions.tsx index 2ee6c110f6ac..d38fbb6c64df 100644 --- a/apps/meteor/client/components/message/content/MessageActions.tsx +++ b/apps/meteor/client/components/message/content/MessageActions.tsx @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { IconProps } from '@rocket.chat/fuselage'; import { Box, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,7 +10,7 @@ import { actionLinks } from '../../../lib/actionLinks'; import MessageAction from './actions/MessageAction'; type MessageActionOptions = { - icon: IconProps['name']; + icon: IconName; i18nLabel?: TranslationKey; label?: string; methodId: string; diff --git a/apps/meteor/client/components/message/content/actions/MessageAction.tsx b/apps/meteor/client/components/message/content/actions/MessageAction.tsx index 9bd565f40fc8..7a787cdd7508 100644 --- a/apps/meteor/client/components/message/content/actions/MessageAction.tsx +++ b/apps/meteor/client/components/message/content/actions/MessageAction.tsx @@ -1,20 +1,20 @@ -import type { IconProps } from '@rocket.chat/fuselage'; -import { Icon, Button } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; -const resolveLegacyIcon = (legacyIcon: IconProps['name'] | `icon-${IconProps['name'] | 'videocam'}`): IconProps['name'] => { +const resolveLegacyIcon = (legacyIcon: IconName | `icon-${IconName | 'videocam'}`): IconName => { if (legacyIcon === 'icon-videocam') { return 'video'; } - return legacyIcon?.replace(/^icon-/, '') as IconProps['name']; + return legacyIcon?.replace(/^icon-/, '') as IconName; }; type MessageActionProps = { - icon: IconProps['name']; + icon: IconName; i18nLabel?: TranslationKey; label?: string; methodId: string; @@ -28,8 +28,7 @@ const MessageAction = ({ icon, methodId, i18nLabel, label, runAction, danger }: const resolvedIcon = resolveLegacyIcon(icon); return ( - ); diff --git a/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx b/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx index 8c72a84f6721..df3b2674b897 100644 --- a/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx +++ b/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx @@ -10,7 +10,7 @@ export const ActionAttachment: FC = ({ actions }) => { const handleLinkClick = useExternalLink(); return ( - + {actions .filter( ({ type, msg_in_chat_window: msgInChatWindow, url, image_url: image, text }) => diff --git a/apps/meteor/client/components/message/content/attachments/default/Field.tsx b/apps/meteor/client/components/message/content/attachments/default/Field.tsx index 9cd2ee32abd2..935e9fb0d9ad 100644 --- a/apps/meteor/client/components/message/content/attachments/default/Field.tsx +++ b/apps/meteor/client/components/message/content/attachments/default/Field.tsx @@ -10,7 +10,7 @@ type FieldProps = { // TODO: description missing color token const Field: FC = ({ title, value, ...props }) => ( - + {title} {value} diff --git a/apps/meteor/client/components/message/content/attachments/structure/Attachment.tsx b/apps/meteor/client/components/message/content/attachments/structure/Attachment.tsx index aa4509f9c7c6..6b1d422eba45 100644 --- a/apps/meteor/client/components/message/content/attachments/structure/Attachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/structure/Attachment.tsx @@ -13,7 +13,7 @@ const Attachment: FC> = (props) => { return ( = ({ retry }) => { } `; return ( - + {t('Retry')} diff --git a/apps/meteor/client/components/message/content/location/MapViewFallback.tsx b/apps/meteor/client/components/message/content/location/MapViewFallback.tsx index 2d7c88f5baa4..34b00d67e0fe 100644 --- a/apps/meteor/client/components/message/content/location/MapViewFallback.tsx +++ b/apps/meteor/client/components/message/content/location/MapViewFallback.tsx @@ -13,7 +13,7 @@ const MapViewFallback: FC = ({ linkUrl }) => { return ( - + {t('Shared_Location')} ); diff --git a/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx b/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx new file mode 100644 index 000000000000..43da25f4f6b5 --- /dev/null +++ b/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx @@ -0,0 +1,40 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { renderHook, act } from '@testing-library/react-hooks'; + +import { useToggleReactionMutation } from './useToggleReactionMutation'; + +it('should be call rest `POST /v1/chat.react` method', async () => { + const fn = jest.fn(); + + const { result, waitFor } = renderHook(() => useToggleReactionMutation(), { + wrapper: mockAppRoot().withEndpoint('POST', '/v1/chat.react', fn).withJohnDoe().build(), + }); + + await act(async () => { + await result.current.mutateAsync({ mid: 'MID', reaction: 'smile' }); + }); + + await waitFor(() => result.current.isLoading === false); + + expect(fn).toHaveBeenCalledWith({ + messageId: 'MID', + reaction: 'smile', + }); +}); + +it('should not work for non-logged in users', async () => { + const fn = jest.fn(); + + const { result } = renderHook(() => useToggleReactionMutation(), { + wrapper: mockAppRoot().withEndpoint('POST', '/v1/chat.react', fn).build(), + }); + + await act(async () => { + expect(result.current.mutateAsync({ mid: 'MID', reaction: 'smile' })).rejects.toThrowError(); + }); + + expect(fn).not.toHaveBeenCalled(); + + expect(result.current.status).toBe('error'); + expect(result.current.error).toEqual(new Error('Not logged in')); +}); diff --git a/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx b/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx index 2955a1eb5b39..550b325012ed 100644 --- a/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx +++ b/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx @@ -39,7 +39,7 @@ const OEmbedPreviewContent = ({ {showSiteName && } - {showFooterSeparator && {'|'}} + {showFooterSeparator && |} {showAuthorName && } diff --git a/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx b/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx index 1227faee6eeb..f56adef8f12d 100644 --- a/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx +++ b/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx @@ -9,7 +9,7 @@ const UrlImagePreview = ({ url }: Pick): ReactElement const { maxHeight: oembedMaxHeight } = useOembedLayout(); return ( - + ); diff --git a/apps/meteor/client/components/message/list/MessageListSkeleton.tsx b/apps/meteor/client/components/message/list/MessageListSkeleton.tsx index 7c780fae5946..5581f5ffc63d 100644 --- a/apps/meteor/client/components/message/list/MessageListSkeleton.tsx +++ b/apps/meteor/client/components/message/list/MessageListSkeleton.tsx @@ -10,11 +10,7 @@ type MessageListSkeletonProps = { const MessageListSkeleton = ({ messageCount = 2 }: MessageListSkeletonProps): ReactElement => { const widths = useMemo( - () => - Array.from( - { length: messageCount }, - () => `${availablePercentualWidths[Math.floor(Math.random() * availablePercentualWidths.length)]}%`, - ), + () => Array.from({ length: messageCount }, (_, index) => `${availablePercentualWidths[index % availablePercentualWidths.length]}%`), [messageCount], ); diff --git a/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx b/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx index 86cc8c58f434..0310cf44c013 100644 --- a/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx +++ b/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx @@ -1,36 +1,6 @@ -import { Tile } from '@rocket.chat/fuselage'; -import { useMergedRefs, usePosition } from '@rocket.chat/fuselage-hooks'; +import { Tile, PositionAnimated } from '@rocket.chat/fuselage'; import type { ReactNode, Ref, RefObject } from 'react'; -import React, { useMemo, useRef, forwardRef } from 'react'; - -const getDropdownContainer = (descendant: HTMLElement | null) => { - for (let element = descendant ?? document.body; element !== document.body; element = element.parentElement ?? document.body) { - if ( - getComputedStyle(element).transform !== 'none' || - getComputedStyle(element).position === 'fixed' || - getComputedStyle(element).willChange === 'transform' - ) { - return element; - } - } - - return document.body; -}; - -const useDropdownPosition = (reference: RefObject, target: RefObject) => { - const innerContainer = getDropdownContainer(reference.current); - const boundingRect = innerContainer.getBoundingClientRect(); - - const { style } = usePosition(reference, target, { - placement: 'bottom-end', - container: innerContainer, - }); - - const left = `${parseFloat(String(style?.left ?? '0')) - boundingRect.left}px`; - const top = `${parseFloat(String(style?.top ?? '0')) - boundingRect.top}px`; - - return useMemo(() => ({ ...style, left, top }), [style, left, top]); -}; +import React, { forwardRef } from 'react'; type DesktopToolboxDropdownProps = { children: ReactNode; @@ -41,15 +11,12 @@ const DesktopToolboxDropdown = forwardRef(function ToolboxDropdownDesktop( { reference, children }: DesktopToolboxDropdownProps, ref: Ref, ) { - const targetRef = useRef(null); - const mergedRef = useMergedRefs(ref, targetRef); - - const style = useDropdownPosition(reference, targetRef); - return ( - - {children} - + + + {children} + + ); }); diff --git a/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx b/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx index b8417cfb6cd0..54a320ebf3d7 100644 --- a/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx +++ b/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx @@ -1,58 +1,95 @@ -import { MessageToolboxItem, Option, OptionDivider, Box } from '@rocket.chat/fuselage'; +import { MessageToolboxItem, Option, OptionDivider, OptionTitle } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps, UIEvent, ReactElement } from 'react'; -import React, { useState, Fragment, useRef } from 'react'; +import type { ComponentProps, MouseEvent, MouseEventHandler, ReactElement } from 'react'; +import React, { Fragment, useCallback, useRef, useState } from 'react'; import type { MessageActionConfig } from '../../../../app/ui-utils/client/lib/MessageAction'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; import ToolboxDropdown from './ToolboxDropdown'; type MessageActionConfigOption = Omit & { - action: (event: UIEvent) => void; + action: ((event: MouseEvent) => void) & MouseEventHandler; }; type MessageActionMenuProps = { + onChangeMenuVisibility: (visible: boolean) => void; options: MessageActionConfigOption[]; }; -const MessageActionMenu = ({ options, ...props }: MessageActionMenuProps): ReactElement => { - const ref = useRef(null); +const getSectionOrder = (section: string): number => { + switch (section) { + case 'communication': + return 0; + case 'interaction': + return 1; + case 'duplication': + return 2; + case 'apps': + return 3; + case 'management': + return 4; + default: + return 5; + } +}; +const MessageActionMenu = ({ options, onChangeMenuVisibility, ...props }: MessageActionMenuProps): ReactElement => { + const buttonRef = useRef(null); const t = useTranslation(); const [visible, setVisible] = useState(false); const isLayoutEmbedded = useEmbeddedLayout(); - const groupOptions = options - .map(({ color, ...option }) => ({ - ...option, - ...(color === 'alert' && { variant: 'danger' as const }), - })) - .reduce((acc, option) => { - const group = option.variant ? option.variant : ''; - acc[group] = acc[group] || []; - if (!(isLayoutEmbedded && option.id === 'reply-directly')) acc[group].push(option); + const handleChangeMenuVisibility = useCallback( + (visible: boolean): void => { + setVisible(visible); + onChangeMenuVisibility(visible); + }, + [onChangeMenuVisibility], + ); + + const groupOptions = options.reduce((acc, option) => { + const { type = '' } = option; + + if (option.color === 'alert') { + option.variant = 'danger' as const; + } + + const order = getSectionOrder(type); + const [sectionType, options] = acc[getSectionOrder(type)] ?? [type, []]; + + if (!(isLayoutEmbedded && option.id === 'reply-directly')) { + options.push(option); + } + + if (options.length === 0) { return acc; - }, {} as { [key: string]: MessageActionConfigOption[] }) as { - [key: string]: MessageActionConfigOption[]; - }; + } + + acc[order] = [sectionType, options]; + + return acc; + }, [] as unknown as [section: string, options: Array][]); + const handleClose = useCallback(() => { + handleChangeMenuVisibility(false); + }, [handleChangeMenuVisibility]); return ( <> setVisible(!visible)} + onClick={(): void => handleChangeMenuVisibility(!visible)} data-qa-id='menu' data-qa-type='message-action-menu' title={t('More')} /> {visible && ( <> - setVisible(!visible)} /> - - {Object.entries(groupOptions).map(([, options], index, arr) => ( + + {groupOptions.map(([section, options], index, arr) => ( + {section === 'apps' && Apps} {options.map((option) => (
    +
    {children}
    diff --git a/apps/meteor/client/views/admin/info/LicenseCard.tsx b/apps/meteor/client/views/admin/info/LicenseCard.tsx index 916fca4c2e09..bccbddaa6db7 100644 --- a/apps/meteor/client/views/admin/info/LicenseCard.tsx +++ b/apps/meteor/client/views/admin/info/LicenseCard.tsx @@ -1,6 +1,6 @@ -import { ButtonGroup, Button, Skeleton, Margins } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button, Skeleton } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { Card } from '@rocket.chat/ui-client'; +import { Card, CardBody, CardCol, CardTitle, CardColSection, CardColTitle, CardFooter } from '@rocket.chat/ui-client'; import { useSetModal, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -43,37 +43,35 @@ const LicenseCard = (): ReactElement => { return ( - {t('License')} - - - + {t('License')} + + + - - - {t('Features')} - - {isLoading ? ( - <> - - - - - - ) : ( - <> - - - - - - - )} - - - - - - + + + {t('Features')} + {isLoading ? ( + <> + + + + + + ) : ( + <> + + + + + + + )} + + + + + {isAirGapped ? ( )} - + ); }; diff --git a/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx b/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx index b0970ec2408b..0ece529fc2d3 100644 --- a/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx +++ b/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx @@ -1,4 +1,4 @@ -import { Modal, Box, ButtonGroup, Button, Scrollable, Callout, Margins, Icon } from '@rocket.chat/fuselage'; +import { Modal, Box, ButtonGroup, Button, Scrollable, Callout, Margins } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, FormEvent, ReactElement } from 'react'; @@ -73,13 +73,13 @@ const OfflineLicenseModal = ({ onClose, license, licenseStatus, ...props }: Offl display='flex' flexDirection='column' alignItems='stretch' - paddingInline='x16' - pb='x8' + paddingInline={16} + pb={8} flexGrow={1} backgroundColor='dark' mb={status === 'invalid' ? 'x8' : undefined} > - + - diff --git a/apps/meteor/client/views/admin/info/UsageCard.tsx b/apps/meteor/client/views/admin/info/UsageCard.tsx index 43d169cf68b8..7a3b2123e5f2 100644 --- a/apps/meteor/client/views/admin/info/UsageCard.tsx +++ b/apps/meteor/client/views/admin/info/UsageCard.tsx @@ -1,7 +1,17 @@ import type { IStats } from '@rocket.chat/core-typings'; import { ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { TextSeparator, Card } from '@rocket.chat/ui-client'; +import { + Card, + CardBody, + CardCol, + CardTitle, + CardColSection, + CardColTitle, + CardFooter, + TextSeparator, + CardIcon, +} from '@rocket.chat/ui-client'; import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; @@ -29,15 +39,15 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { return ( - {t('Usage')} - - - - {t('Users')} + {t('Usage')} + + + + {t('Users')} - {t('Total')} + {t('Total')} } value={statistics.totalUsers} @@ -45,9 +55,9 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - + - {' '} + {' '} {t('Online')} } @@ -56,9 +66,9 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - + - {' '} + {' '} {t('Busy')} } @@ -67,9 +77,9 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - + - {' '} + {' '} {t('Away')} } @@ -78,37 +88,34 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - + - {' '} + {' '} {t('Offline')} } value={statistics.offlineUsers} /> - - - {t('Types_and_Distribution')} + + + {t('Types_and_Distribution')} - - - {t('Uploads')} + + + {t('Uploads')} - - - - - - {t('Total_rooms')} + + + {t('Total_rooms')} - {t('Stats_Total_Rooms')} + {t('Stats_Total_Rooms')} } value={statistics.totalRooms} @@ -116,7 +123,7 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - {t('Stats_Total_Channels')} + {t('Stats_Total_Channels')} } value={statistics.totalChannels} @@ -124,7 +131,7 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - {t('Stats_Total_Private_Groups')} + {t('Stats_Total_Private_Groups')} } value={statistics.totalPrivateGroups} @@ -132,7 +139,7 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - {t('Stats_Total_Direct_Messages')} + {t('Stats_Total_Direct_Messages')} } value={statistics.totalDirect} @@ -140,7 +147,7 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - {t('Total_Discussions')} + {t('Total_Discussions')} } value={statistics.totalDiscussions} @@ -148,30 +155,30 @@ const UsageCard = ({ statistics, vertical }: UsageCardProps): ReactElement => { - {t('Stats_Total_Livechat_Rooms')} + {t('Stats_Total_Livechat_Rooms')} } value={statistics.totalLivechat} /> - - - {t('Total_messages')} + + + {t('Total_messages')} - - - - + + + + - + ); }; diff --git a/apps/meteor/client/views/admin/info/UsagePieGraph.tsx b/apps/meteor/client/views/admin/info/UsagePieGraph.tsx index d6f266eb875f..935651555e71 100644 --- a/apps/meteor/client/views/admin/info/UsagePieGraph.tsx +++ b/apps/meteor/client/views/admin/info/UsagePieGraph.tsx @@ -60,7 +60,7 @@ const UsageGraph = ({ used = 0, total = 0, label, color, size }: UsageGraphProps return ( - + {' '} / {unlimited ? '∞' : total} - + {label} diff --git a/apps/meteor/client/views/admin/integrations/IncomingWebhookForm.js b/apps/meteor/client/views/admin/integrations/IncomingWebhookForm.js index 94bbd156b86c..ae4d4fa411b5 100644 --- a/apps/meteor/client/views/admin/integrations/IncomingWebhookForm.js +++ b/apps/meteor/client/views/admin/integrations/IncomingWebhookForm.js @@ -1,4 +1,4 @@ -import { Field, TextInput, Box, ToggleSwitch, Icon, TextAreaInput, FieldGroup, Margins } from '@rocket.chat/fuselage'; +import { Field, TextInput, Box, ToggleSwitch, Icon, TextAreaInput, FieldGroup, Margins, Select } from '@rocket.chat/fuselage'; import { useAbsoluteUrl, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo, useCallback } from 'react'; @@ -11,7 +11,8 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat const absoluteUrl = useAbsoluteUrl(); - const { enabled, channel, username, name, alias, avatar, emoji, scriptEnabled, script, overrideDestinationChannelEnabled } = formValues; + const { enabled, channel, username, name, alias, avatar, emoji, scriptEnabled, script, scriptEngine, overrideDestinationChannelEnabled } = + formValues; const { handleEnabled, @@ -24,6 +25,7 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat handleScriptEnabled, handleOverrideDestinationChannelEnabled, handleScript, + handleScriptEngine, } = formHandlers; const url = absoluteUrl(`hooks/${extraData._id}/${extraData.token}`); @@ -42,6 +44,14 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat url, }); + const scriptEngineOptions = useMemo( + () => [ + ['vm2', t('Script_Engine_vm2')], + ['isolated-vm', t('Script_Engine_isolated_vm')], + ], + [t], + ); + const hilightedExampleJson = useHighlightedCode('json', JSON.stringify(exampleData, null, 2)); return ( @@ -172,6 +182,18 @@ export default function IncomingWebhookForm({ formValues, formHandlers, extraDat ), [t, scriptEnabled, handleScriptEnabled], )} + {useMemo( + () => ( + + {t('Script_Engine')} + + + + {t('Script_Engine_Description')} + + ), + [scriptEngine, scriptEngineOptions, handleScriptEngine, t], + )} {useMemo( () => ( diff --git a/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhook.js b/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhook.js index 7de5944b48cc..e785f63ca29d 100644 --- a/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhook.js +++ b/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhook.js @@ -17,6 +17,7 @@ const getInitialValue = (data) => { avatar: data.avatar ?? '', emoji: data.emoji ?? '', scriptEnabled: data.scriptEnabled, + scriptEngine: data.scriptEngine ?? 'vm2', overrideDestinationChannelEnabled: data.overrideDestinationChannelEnabled, script: data.script, }; @@ -85,7 +86,7 @@ function EditIncomingWebhook({ data, onChange, ...props }) {
    - diff --git a/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhookWithData.js b/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhookWithData.js index a876d6565903..cccdc346ccd7 100644 --- a/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhookWithData.js +++ b/apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhookWithData.js @@ -33,20 +33,20 @@ function EditIncomingWebhookWithData({ integrationId, ...props }) { if (isLoading) { return ( - - - - - - - + + + + + + + ); } if (error) { return ( - + {t('Oops_page_not_found')} ); diff --git a/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js b/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js index ce3d0ece244e..1a4e246d09cf 100644 --- a/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js +++ b/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; @@ -26,8 +26,8 @@ function EditIntegrationsPage({ ...props }) { - {type === 'outgoing' && } diff --git a/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhook.js b/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhook.js index b7e63063cf39..383b9209519d 100644 --- a/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhook.js +++ b/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhook.js @@ -24,6 +24,7 @@ const getInitialValue = (data) => { avatar: data.avatar ?? '', emoji: data.emoji ?? '', scriptEnabled: data.scriptEnabled ?? false, + scriptEngine: data.scriptEngine ?? 'vm2', script: data.script ?? '', retryFailedCalls: data.retryFailedCalls ?? true, retryCount: data.retryCount ?? 5, @@ -104,7 +105,7 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) { - diff --git a/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhookWithData.js b/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhookWithData.js index 8aa0ed79b803..5eac5b2fdc22 100644 --- a/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhookWithData.js +++ b/apps/meteor/client/views/admin/integrations/edit/EditOutgoingWebhookWithData.js @@ -33,20 +33,20 @@ function EditOutgoingWebhookWithData({ integrationId, ...props }) { if (isLoading) { return ( - - - - - - - + + + + + + + ); } if (error) { return ( - + {t('Oops_page_not_found')} ); diff --git a/apps/meteor/client/views/admin/integrations/edit/HistoryContent.tsx b/apps/meteor/client/views/admin/integrations/edit/HistoryContent.tsx index e88efa0bdf0e..255205352323 100644 --- a/apps/meteor/client/views/admin/integrations/edit/HistoryContent.tsx +++ b/apps/meteor/client/views/admin/integrations/edit/HistoryContent.tsx @@ -11,19 +11,19 @@ function HistoryContent({ data, isLoading }: { data: Serialized - - - - - - + + + + + + + ); } if (data.length < 1) { - return {t('Integration_Outgoing_WebHook_No_History')}; + return {t('Integration_Outgoing_WebHook_No_History')}; } return ( diff --git a/apps/meteor/client/views/admin/integrations/edit/HistoryItem.js b/apps/meteor/client/views/admin/integrations/edit/HistoryItem.js index bbe4b0f38855..426a79c9c473 100644 --- a/apps/meteor/client/views/admin/integrations/edit/HistoryItem.js +++ b/apps/meteor/client/views/admin/integrations/edit/HistoryItem.js @@ -53,7 +53,7 @@ function HistoryItem({ data, ...props }) { title={ - + {formatDateAndTime(_createdAt)} - diff --git a/apps/meteor/client/views/admin/integrations/new/NewBot.js b/apps/meteor/client/views/admin/integrations/new/NewBot.js index b8aeacb65e93..982a0e65a2c1 100644 --- a/apps/meteor/client/views/admin/integrations/new/NewBot.js +++ b/apps/meteor/client/views/admin/integrations/new/NewBot.js @@ -4,5 +4,5 @@ import React from 'react'; export default function NewBot() { const t = useTranslation(); - return ; + return ; } diff --git a/apps/meteor/client/views/admin/integrations/new/NewIncomingWebhook.js b/apps/meteor/client/views/admin/integrations/new/NewIncomingWebhook.js index 019dc6d0d730..7b4e0880e57f 100644 --- a/apps/meteor/client/views/admin/integrations/new/NewIncomingWebhook.js +++ b/apps/meteor/client/views/admin/integrations/new/NewIncomingWebhook.js @@ -15,6 +15,7 @@ const initialState = { avatar: '', emoji: '', scriptEnabled: false, + scriptEngine: 'isolated-vm', overrideDestinationChannelEnabled: false, script: '', }; diff --git a/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js b/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js index a5456ee9f255..1279cf29cb74 100644 --- a/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js +++ b/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js @@ -1,4 +1,4 @@ -import { Tabs, Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Tabs, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; @@ -28,8 +28,8 @@ export default function NewIntegrationsPage({ ...props }) { - diff --git a/apps/meteor/client/views/admin/integrations/new/NewOutgoingWebhook.js b/apps/meteor/client/views/admin/integrations/new/NewOutgoingWebhook.js index 818082f5f5de..153dc4c6eb7f 100644 --- a/apps/meteor/client/views/admin/integrations/new/NewOutgoingWebhook.js +++ b/apps/meteor/client/views/admin/integrations/new/NewOutgoingWebhook.js @@ -23,6 +23,7 @@ const defaultData = { avatar: '', emoji: '', scriptEnabled: false, + scriptEngine: 'isolated-vm', script: '', retryFailedCalls: true, retryCount: 6, diff --git a/apps/meteor/client/views/admin/integrations/new/NewZapier.js b/apps/meteor/client/views/admin/integrations/new/NewZapier.js index 59d1b67e4f50..5bb46c86ba6f 100644 --- a/apps/meteor/client/views/admin/integrations/new/NewZapier.js +++ b/apps/meteor/client/views/admin/integrations/new/NewZapier.js @@ -33,7 +33,7 @@ export default function NewZapier({ ...props }) { return ( <> - + {t('Install_Zapier_from_marketplace')} {!script && ( diff --git a/apps/meteor/client/views/admin/mailer/MailerPage.tsx b/apps/meteor/client/views/admin/mailer/MailerPage.tsx index d35be2d1f50c..0f47d8ef17ce 100644 --- a/apps/meteor/client/views/admin/mailer/MailerPage.tsx +++ b/apps/meteor/client/views/admin/mailer/MailerPage.tsx @@ -1,106 +1,179 @@ -import { TextInput, TextAreaInput, Field, FieldGroup, CheckBox, Button, ButtonGroup } from '@rocket.chat/fuselage'; -import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { SyntheticEvent } from 'react'; -import React, { useState, useCallback } from 'react'; +import { TextInput, TextAreaInput, Field, FieldGroup, CheckBox, Button, ButtonGroup, Box } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; +import React from 'react'; +import { Controller, useForm } from 'react-hook-form'; import { validateEmail } from '../../../../lib/emailValidator'; import { isJSON } from '../../../../lib/utils/isJSON'; import Page from '../../../components/Page'; -import { useSendMail } from './useSendMail'; + +export type SendEmailFormValue = { + fromEmail: string; + subject: string; + emailBody: string; + dryRun: boolean; + query?: string; +}; + +const initialData = { fromEmail: '', query: '', dryRun: false, subject: '', emailBody: '' }; const MailerPage = () => { const t = useTranslation(); - const sendMail = useSendMail(); + const dispatchToastMessage = useToastMessageDispatch(); + + const { + register, + formState: { errors, isDirty }, + handleSubmit, + reset, + control, + } = useForm({ defaultValues: initialData, mode: 'onBlur' }); + + const mailerEndpoint = useEndpoint('POST', '/v1/mailer'); + const sendMailAction = useMutation({ + mutationFn: mailerEndpoint, + onSuccess: () => { + dispatchToastMessage({ + type: 'success', + message: t('The_emails_are_being_sent'), + }); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + }); - const [fromEmail, setFromEmail] = useState<{ value: string; error?: string }>({ value: '' }); - const [dryRun, setDryRun] = useState(false); - const [query, setQuery] = useState<{ value: string; error?: string }>({ value: '' }); - const [subject, setSubject] = useState(''); - const [emailBody, setEmailBody] = useState(''); + const handleSendEmail = async ({ fromEmail, subject, emailBody, dryRun, query }: SendEmailFormValue) => { + sendMailAction.mutateAsync({ from: fromEmail, subject, body: emailBody, dryrun: dryRun, query }); + }; + + const mailerFormId = useUniqueId(); + const fromEmailId = useUniqueId(); + const queryId = useUniqueId(); + const dryRunId = useUniqueId(); + const subjectId = useUniqueId(); + const emailBodyId = useUniqueId(); return ( - - - + - - - e.preventDefault(), [])} method='post'> - - {t('From')} - - ): void => { - setFromEmail({ - value: e.currentTarget.value, - error: !validateEmail(e.currentTarget.value) ? t('Invalid_email') : undefined, - }); - }} - /> - - - - - setDryRun(!dryRun)} /> - {t('Dry_run')} - - {t('Dry_run_description')} - - - {t('Query')} - - ): void => { - setQuery({ - value: e.currentTarget.value, - error: e.currentTarget.value && !isJSON(e.currentTarget.value) ? t('Invalid_JSON') : undefined, - }); - }} - /> - - {t('Query_description')} - - - {t('Subject')} - - ): void => { - setSubject(e.currentTarget.value); - }} - /> - - - - {t('Email_body')} - - ): void => setEmailBody(e.currentTarget.value)} - /> - - - - - + ); }; diff --git a/apps/meteor/client/views/admin/mailer/useSendMail.ts b/apps/meteor/client/views/admin/mailer/useSendMail.ts deleted file mode 100644 index d5e7c967e941..000000000000 --- a/apps/meteor/client/views/admin/mailer/useSendMail.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; - -type sendMailObject = { - fromEmail: { value: string; error?: string }; - subject: string; - emailBody: string; - dryRun: boolean; - query: { value: string; error?: string }; -}; - -type useSendMailType = () => ({ fromEmail, subject, emailBody, dryRun, query }: sendMailObject) => void; - -export const useSendMail: useSendMailType = () => { - const t = useTranslation(); - const meteorSendMail = useEndpoint('POST', '/v1/mailer'); - const dispatchToastMessage = useToastMessageDispatch(); - - return ({ fromEmail, subject, emailBody, dryRun, query }): void => { - if (query.error) { - dispatchToastMessage({ - type: 'error', - message: t('Query_is_not_valid_JSON'), - }); - return; - } - if (fromEmail.error || fromEmail.value.length < 1) { - dispatchToastMessage({ - type: 'error', - message: t('error-invalid-from-address'), - }); - return; - } - if (emailBody.indexOf('[unsubscribe]') === -1) { - dispatchToastMessage({ - type: 'error', - message: t('error-missing-unsubscribe-link'), - }); - return; - } - - meteorSendMail({ from: fromEmail.value, subject, body: emailBody, dryrun: dryRun, query: query.value }); - dispatchToastMessage({ - type: 'success', - message: t('The_emails_are_being_sent'), - }); - }; -}; diff --git a/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx b/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx index 75ab9b9e96db..603afa54f6e8 100644 --- a/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx +++ b/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx @@ -1,8 +1,9 @@ -import { Button, Icon, Menu, Option, ButtonGroup } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import type { FC } from 'react'; +import GenericMenu from '../../../components/GenericMenu/GenericMenu'; import useDeactivateUserAction from './hooks/useDeactivateUserAction'; import useDeleteMessagesAction from './hooks/useDeleteMessagesAction'; import useDismissUserAction from './hooks/useDismissUserAction'; @@ -10,23 +11,32 @@ import useResetAvatarAction from './hooks/useResetAvatarAction'; const MessageContextFooter: FC<{ userId: string; deleted: boolean }> = ({ userId, deleted }) => { const t = useTranslation(); - const { action } = useDeleteMessagesAction(userId); + + const dismissUserAction = useDismissUserAction(userId); + const deleteMessagesAction = useDeleteMessagesAction(userId); return ( - - + - ( -