From 7bafe0390ea5f05084215c977c5604e09a605aa8 Mon Sep 17 00:00:00 2001 From: Lucas Pelegrino Date: Tue, 17 Dec 2024 18:11:19 -0300 Subject: [PATCH 1/3] fix: Messages not being processed for all slack servers (#34148) --- .changeset/lovely-beers-argue.md | 5 ++++ .../app/slackbridge/server/RocketAdapter.js | 23 +++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 .changeset/lovely-beers-argue.md diff --git a/.changeset/lovely-beers-argue.md b/.changeset/lovely-beers-argue.md new file mode 100644 index 000000000000..a01a97535e48 --- /dev/null +++ b/.changeset/lovely-beers-argue.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes messages not being processed for all slack servers diff --git a/apps/meteor/app/slackbridge/server/RocketAdapter.js b/apps/meteor/app/slackbridge/server/RocketAdapter.js index 245e28c72203..70418d407443 100644 --- a/apps/meteor/app/slackbridge/server/RocketAdapter.js +++ b/apps/meteor/app/slackbridge/server/RocketAdapter.js @@ -62,8 +62,9 @@ export default class RocketAdapter { try { if (!slack.getSlackChannel(rocketMessageDeleted.rid)) { // This is on a channel that the rocket bot is not subscribed on this slack server - return; + continue; } + rocketLogger.debug('onRocketMessageDelete', rocketMessageDeleted); await slack.postDeleteMessage(rocketMessageDeleted); } catch (err) { @@ -134,22 +135,23 @@ export default class RocketAdapter { try { if (!slack.getSlackChannel(rocketMessage.rid)) { // This is on a channel that the rocket bot is not subscribed - return; + continue; } rocketLogger.debug('onRocketMessage', rocketMessage); if (rocketMessage.editedAt) { // This is an Edit Event await this.processMessageChanged(rocketMessage, slack); - return rocketMessage; + continue; } // Ignore messages originating from Slack if (rocketMessage._id.indexOf('slack-') === 0) { - return rocketMessage; + continue; } if (rocketMessage.file) { - return this.processFileShare(rocketMessage, slack); + await this.processFileShare(rocketMessage, slack); + continue; } // A new message from Rocket.Chat @@ -206,10 +208,7 @@ export default class RocketAdapter { } } - const message = `${text} ${fileName}`; - - rocketMessage.msg = message; - await slack.postMessage(slack.getSlackChannel(rocketMessage.rid), rocketMessage); + await slack.postMessage(slack.getSlackChannel(rocketMessage.rid), { ...rocketMessage, msg: `${text} ${fileName}` }); } } @@ -266,7 +265,7 @@ export default class RocketAdapter { for await (const slack of this.slackAdapters) { if (addedRoom) { - return; + continue; } const slackChannel = await slack.slackAPI.getRoomInfo(slackChannelID); @@ -274,7 +273,7 @@ export default class RocketAdapter { const members = await slack.slackAPI.getMembers(slackChannelID); if (!members) { rocketLogger.error('Could not fetch room members'); - return; + continue; } const rocketRoom = await Rooms.findOneByName(slackChannel.name); @@ -288,7 +287,7 @@ export default class RocketAdapter { if (!rocketUserCreator) { rocketLogger.error({ msg: 'Could not fetch room creator information', creator: slackChannel.creator }); - return; + continue; } try { From 75a14b2e013aca7361cac56316f2b7e8c07d9dc8 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 17 Dec 2024 18:27:04 -0300 Subject: [PATCH 2/3] feat: Adds callout from cloud for subscription upgrade eligibility (#33549) Co-authored-by: Aleksander Nicacio da Silva Co-authored-by: Lucas Pelegrino Co-authored-by: Kevin Aleman --- .changeset/wicked-socks-hide.md | 7 ++ .../fetchWorkspaceSyncPayload.ts | 42 ++++++++ .../functions/syncWorkspace/syncCloudData.ts | 55 ++-------- .../components/Page/PageHeaderNoShadow.tsx | 2 +- apps/meteor/client/hooks/useLicense.ts | 15 ++- .../uikit/hooks/useBannerContextValue.ts | 1 + .../admin/subscription/SubscriptionPage.tsx | 20 ++-- .../surface/UiKitSubscriptionLicense.tsx | 102 ++++++++++++++++++ .../UiKitSubscriptionLicenseSurface.tsx | 33 ++++++ apps/meteor/ee/server/api/licenses.ts | 23 +++- .../core-apps/cloudAnnouncements.module.ts | 5 +- .../cloudSubscriptionCommunication.module.ts | 37 +++++++ apps/meteor/server/settings/setup-wizard.ts | 5 + apps/meteor/server/startup/coreApps.ts | 2 + .../syncWorkspace/syncCloudData.spec.ts | 101 +++++++++++++++++ .../src/cloud/CloudSyncAnnouncement.ts | 10 ++ .../src/cloud/WorkspaceSyncPayload.ts | 1 + packages/core-typings/src/cloud/index.ts | 1 + .../core-typings/src/license/LicenseInfo.ts | 2 + packages/rest-typings/src/v1/licenses.ts | 3 +- 20 files changed, 407 insertions(+), 60 deletions(-) create mode 100644 .changeset/wicked-socks-hide.md create mode 100644 apps/meteor/app/cloud/server/functions/syncWorkspace/fetchWorkspaceSyncPayload.ts create mode 100644 apps/meteor/client/views/admin/subscription/surface/UiKitSubscriptionLicense.tsx create mode 100644 apps/meteor/client/views/admin/subscription/surface/UiKitSubscriptionLicenseSurface.tsx create mode 100644 apps/meteor/server/modules/core-apps/cloudSubscriptionCommunication.module.ts create mode 100644 apps/meteor/tests/unit/app/cloud/server/functions/syncWorkspace/syncCloudData.spec.ts create mode 100644 packages/core-typings/src/cloud/CloudSyncAnnouncement.ts diff --git a/.changeset/wicked-socks-hide.md b/.changeset/wicked-socks-hide.md new file mode 100644 index 000000000000..3f425c3228e2 --- /dev/null +++ b/.changeset/wicked-socks-hide.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/core-typings": minor +"@rocket.chat/rest-typings": minor +--- + +Adds a new callout in the subscription page to inform users of subscription upgrade eligibility when applicable. diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/fetchWorkspaceSyncPayload.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/fetchWorkspaceSyncPayload.ts new file mode 100644 index 000000000000..7d01f6305c83 --- /dev/null +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/fetchWorkspaceSyncPayload.ts @@ -0,0 +1,42 @@ +import type { Cloud, Serialized } from '@rocket.chat/core-typings'; +import { serverFetch as fetch } from '@rocket.chat/server-fetch'; +import { v, compile } from 'suretype'; + +import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError'; +import { settings } from '../../../../settings/server'; + +const workspaceSyncPayloadSchema = v.object({ + workspaceId: v.string().required(), + publicKey: v.string(), + license: v.string().required(), +}); + +const assertWorkspaceSyncPayload = compile(workspaceSyncPayloadSchema); + +export async function fetchWorkspaceSyncPayload({ + token, + data, +}: { + token: string; + data: Cloud.WorkspaceSyncRequestPayload; +}): Promise> { + const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); + const response = await fetch(`${workspaceRegistrationClientUri}/sync`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, + body: data, + }); + + if (!response.ok) { + const { error } = await response.json(); + throw new CloudWorkspaceConnectionError(`Failed to connect to Rocket.Chat Cloud: ${error}`); + } + + const payload = await response.json(); + + assertWorkspaceSyncPayload(payload); + + return payload; +} diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts index fc55fc9e34fd..f28821b08387 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts @@ -1,57 +1,14 @@ -import type { Cloud, Serialized } from '@rocket.chat/core-typings'; import { DuplicatedLicenseError } from '@rocket.chat/license'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { v, compile } from 'suretype'; +import { Settings } from '@rocket.chat/models'; import { callbacks } from '../../../../../lib/callbacks'; import { CloudWorkspaceAccessError } from '../../../../../lib/errors/CloudWorkspaceAccessError'; -import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError'; import { SystemLogger } from '../../../../../server/lib/logger/system'; -import { settings } from '../../../../settings/server'; import { buildWorkspaceRegistrationData } from '../buildRegistrationData'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus'; - -const workspaceSyncPayloadSchema = v.object({ - workspaceId: v.string().required(), - publicKey: v.string(), - license: v.string().required(), -}); - -const assertWorkspaceSyncPayload = compile(workspaceSyncPayloadSchema); - -const fetchWorkspaceSyncPayload = async ({ - token, - data, -}: { - token: string; - data: Cloud.WorkspaceSyncRequestPayload; -}): Promise> => { - const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); - const response = await fetch(`${workspaceRegistrationClientUri}/sync`, { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - }, - body: data, - }); - - if (!response.ok) { - try { - const { error } = await response.json(); - throw new CloudWorkspaceConnectionError(`Failed to connect to Rocket.Chat Cloud: ${error}`); - } catch (error) { - throw new CloudWorkspaceConnectionError(`Failed to connect to Rocket.Chat Cloud: ${response.statusText}`); - } - } - - const payload = await response.json(); - - assertWorkspaceSyncPayload(payload); - - return payload; -}; +import { fetchWorkspaceSyncPayload } from './fetchWorkspaceSyncPayload'; export async function syncCloudData() { try { @@ -67,11 +24,17 @@ export async function syncCloudData() { const workspaceRegistrationData = await buildWorkspaceRegistrationData(undefined); - const { license, removeLicense = false } = await fetchWorkspaceSyncPayload({ + const { + license, + removeLicense = false, + cloudSyncAnnouncement, + } = await fetchWorkspaceSyncPayload({ token, data: workspaceRegistrationData, }); + await Settings.updateValueById('Cloud_Sync_Announcement_Payload', JSON.stringify(cloudSyncAnnouncement ?? null)); + if (removeLicense) { await callbacks.run('workspaceLicenseRemoved'); } else { diff --git a/apps/meteor/client/components/Page/PageHeaderNoShadow.tsx b/apps/meteor/client/components/Page/PageHeaderNoShadow.tsx index 51feca504834..afc3891a5714 100644 --- a/apps/meteor/client/components/Page/PageHeaderNoShadow.tsx +++ b/apps/meteor/client/components/Page/PageHeaderNoShadow.tsx @@ -22,7 +22,7 @@ const PageHeaderNoShadow = ({ children = undefined, title, onClickBack, ...props useDocumentTitle(typeof title === 'string' ? title : undefined); return ( - + ({ }; export const useLicense = (params?: LicenseParams) => { - return useLicenseBase({ params, select: (data) => data.license }); + return useLicenseBase({ + params, + select: (data) => data.license, + }); +}; + +export const useLicenseWithCloudAnnouncement = (params?: LicenseParams) => { + return useLicenseBase({ + params, + select: ({ license, cloudSyncAnnouncement }) => ({ + ...license, + cloudSyncAnnouncement, + }), + }); }; export const useHasLicense = (): UseQueryResult => { diff --git a/apps/meteor/client/uikit/hooks/useBannerContextValue.ts b/apps/meteor/client/uikit/hooks/useBannerContextValue.ts index 35b38e8dee63..81cb70af6636 100644 --- a/apps/meteor/client/uikit/hooks/useBannerContextValue.ts +++ b/apps/meteor/client/uikit/hooks/useBannerContextValue.ts @@ -41,6 +41,7 @@ export const useBannerContextValue = ({ view, values }: UseBannerContextValuePar }, updateState: (): void => undefined, appId: view.appId, + viewId: view.viewId, values, }; }; diff --git a/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx b/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx index e6f5beffe7ba..aaf4f984c89e 100644 --- a/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx +++ b/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx @@ -21,9 +21,12 @@ import PlanCardCommunity from './components/cards/PlanCard/PlanCardCommunity'; import SeatsCard from './components/cards/SeatsCard'; import { useCancelSubscriptionModal } from './hooks/useCancelSubscriptionModal'; import { useWorkspaceSync } from './hooks/useWorkspaceSync'; -import { Page, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page'; +import UiKitSubscriptionLicense from './surface/UiKitSubscriptionLicense'; +import { Page, PageScrollableContentWithShadow } from '../../../components/Page'; +import PageBlockWithBorder from '../../../components/Page/PageBlockWithBorder'; +import PageHeaderNoShadow from '../../../components/Page/PageHeaderNoShadow'; import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; -import { useInvalidateLicense, useLicense } from '../../../hooks/useLicense'; +import { useInvalidateLicense, useLicenseWithCloudAnnouncement } from '../../../hooks/useLicense'; import { useRegistrationStatus } from '../../../hooks/useRegistrationStatus'; function useShowLicense() { @@ -48,7 +51,7 @@ const SubscriptionPage = () => { const router = useRouter(); const { data: enterpriseData } = useIsEnterprise(); const { isRegistered } = useRegistrationStatus(); - const { data: licensesData, isLoading: isLicenseLoading } = useLicense({ loadValues: true }); + const { data: licensesData, isLoading: isLicenseLoading } = useLicenseWithCloudAnnouncement({ loadValues: true }); const syncLicenseUpdate = useWorkspaceSync(); const invalidateLicenseQuery = useInvalidateLicense(); @@ -56,7 +59,7 @@ const SubscriptionPage = () => { const showSubscriptionCallout = useDebouncedValue(subscriptionSuccess || syncLicenseUpdate.isLoading, 10000); - const { license, limits, activeModules = [] } = licensesData || {}; + const { license, limits, activeModules = [], cloudSyncAnnouncement } = licensesData || {}; const { isEnterprise = true } = enterpriseData || {}; const getKeyLimit = (key: 'monthlyActiveContacts' | 'activeUsers') => { @@ -99,7 +102,7 @@ const SubscriptionPage = () => { return ( - + {isRegistered && (