Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Cloud, Serialized } from '@rocket.chat/core-typings';
import { Settings } from '@rocket.chat/models';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { v, compile } from 'suretype';
import { z } from 'zod';

import { callbacks } from '../../../../lib/callbacks';
import { CloudWorkspaceConnectionError } from '../../../../lib/errors/CloudWorkspaceConnectionError';
Expand All @@ -11,17 +11,14 @@ import { settings } from '../../../settings/server';
import { LICENSE_VERSION } from '../license';
import { getWorkspaceAccessToken } from './getWorkspaceAccessToken';

const workspaceLicensePayloadSchema = v.object({
version: v.number().required(),
address: v.string().required(),
license: v.string().required(),
updatedAt: v.string().format('date-time').required(),
modules: v.string().required(),
expireAt: v.string().format('date-time').required(),
const workspaceLicensePayloadSchema = z.object({
version: z.number(),
address: z.string(),
license: z.string(),
updatedAt: z.string().datetime(),
expireAt: z.string().datetime(),
});

const assertWorkspaceLicensePayload = compile(workspaceLicensePayloadSchema);

const fetchCloudWorkspaceLicensePayload = async ({ token }: { token: string }): Promise<Serialized<Cloud.WorkspaceLicensePayload>> => {
const workspaceRegistrationClientUri = settings.get<string>('Cloud_Workspace_Registration_Client_Uri');
const response = await fetch(`${workspaceRegistrationClientUri}/license`, {
Expand All @@ -44,14 +41,19 @@ const fetchCloudWorkspaceLicensePayload = async ({ token }: { token: string }):

const payload = await response.json();

assertWorkspaceLicensePayload(payload);
const assertWorkspaceLicensePayload = workspaceLicensePayloadSchema.safeParse(payload);

if (!assertWorkspaceLicensePayload.success) {
console.error('workspaceLicensePayloadSchema failed type validation', assertWorkspaceLicensePayload.error.errors);
}

return payload;
};

export async function getWorkspaceLicense() {
const currentLicense = await Settings.findOne('Cloud_Workspace_License');
// it should never happen, since even if the license is not found, it will return an empty settings

if (!currentLicense?._updatedAt) {
throw new CloudWorkspaceLicenseError('Failed to retrieve current license');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type Cloud, type Serialized } from '@rocket.chat/core-typings';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { v, compile } from 'suretype';
import { z } from 'zod';

import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError';
import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError';
Expand All @@ -11,38 +11,38 @@ import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '..
import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus';
import { handleAnnouncementsOnWorkspaceSync, handleNpsOnWorkspaceSync } from './handleCommsSync';

const workspaceCommPayloadSchema = v.object({
workspaceId: v.string().required(),
publicKey: v.string(),
nps: v.object({
id: v.string().required(),
startAt: v.string().format('date-time').required(),
expireAt: v.string().format('date-time').required(),
}),
announcements: v.object({
create: v.array(
v.object({
_id: v.string().required(),
_updatedAt: v.string().format('date-time').required(),
selector: v.object({
roles: v.array(v.string()),
const workspaceCommPayloadSchema = z.object({
workspaceId: z.string().optional(),
publicKey: z.string().optional(),
nps: z
.object({
id: z.string(),
startAt: z.string().datetime(),
expireAt: z.string().datetime(),
})
.optional(),
announcements: z.object({
create: z.array(
z.object({
_id: z.string(),
_updatedAt: z.string().datetime().optional(),
selector: z.object({
roles: z.array(z.string()),
}),
platform: v.array(v.string().enum('web', 'mobile')).required(),
expireAt: v.string().format('date-time').required(),
startAt: v.string().format('date-time').required(),
createdBy: v.string().enum('cloud', 'system').required(),
createdAt: v.string().format('date-time').required(),
dictionary: v.object({}).additional(v.object({}).additional(v.string())),
view: v.any(),
surface: v.string().enum('banner', 'modal').required(),
platform: z.array(z.enum(['web', 'mobile'])),
expireAt: z.string().datetime(),
startAt: z.string().datetime(),
createdBy: z.enum(['cloud', 'system']),
createdAt: z.string().datetime(),
dictionary: z.record(z.record(z.string())).optional(),
view: z.unknown(),
surface: z.enum(['banner', 'modal']),
}),
),
delete: v.array(v.string()),
delete: z.array(z.string()).optional(),
}),
});

const assertWorkspaceCommPayload = compile(workspaceCommPayloadSchema);

const fetchCloudAnnouncementsSync = async ({
token,
data,
Expand Down Expand Up @@ -70,7 +70,12 @@ const fetchCloudAnnouncementsSync = async ({

const payload = await response.json();

assertWorkspaceCommPayload(payload);
const assertWorkspaceCommPayload = workspaceCommPayloadSchema.safeParse(payload);

if (!assertWorkspaceCommPayload.success) {
console.error('workspaceCommPayloadSchema failed type validation', assertWorkspaceCommPayload.error.errors);
}

return payload;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import type { Cloud, Serialized } from '@rocket.chat/core-typings';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { v, compile } from 'suretype';
import { z } from 'zod';

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 workspaceSyncPayloadSchema = z.object({
workspaceId: z.string(),
publicKey: z.string().optional(),
license: z.string(),
});

const assertWorkspaceSyncPayload = compile(workspaceSyncPayloadSchema);

export async function fetchWorkspaceSyncPayload({
token,
data,
Expand All @@ -36,7 +34,11 @@ export async function fetchWorkspaceSyncPayload({

const payload = await response.json();

assertWorkspaceSyncPayload(payload);
const assertWorkspaceSyncPayload = workspaceSyncPayloadSchema.safeParse(payload);

if (!assertWorkspaceSyncPayload.success) {
console.error('workspaceSyncPayloadSchema failed type validation', assertWorkspaceSyncPayload.error.errors);
}

return payload;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Cloud, type Serialized } from '@rocket.chat/core-typings';
import { Settings } from '@rocket.chat/models';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { v, compile } from 'suretype';
import { z } from 'zod';

import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError';
import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError';
Expand All @@ -14,76 +14,72 @@ import { getWorkspaceLicense } from '../getWorkspaceLicense';
import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus';
import { handleBannerOnWorkspaceSync, handleNpsOnWorkspaceSync } from './handleCommsSync';

const workspaceClientPayloadSchema = v.object({
workspaceId: v.string().required(),
publicKey: v.string(),
trial: v.object({
trialing: v.boolean().required(),
trialID: v.string().required(),
endDate: v.string().format('date-time').required(),
marketing: v
.object({
utmContent: v.string().required(),
utmMedium: v.string().required(),
utmSource: v.string().required(),
utmCampaign: v.string().required(),
})
.required(),
DowngradesToPlan: v
.object({
id: v.string().required(),
})
.required(),
trialRequested: v.boolean().required(),
}),
nps: v.object({
id: v.string().required(),
startAt: v.string().format('date-time').required(),
expireAt: v.string().format('date-time').required(),
const workspaceClientPayloadSchema = z.object({
workspaceId: z.string(),
publicKey: z.string().optional(),
trial: z
.object({
trialing: z.boolean(),
trialID: z.string(),
endDate: z.string().datetime(),
marketing: z.object({
utmContent: z.string(),
utmMedium: z.string(),
utmSource: z.string(),
utmCampaign: z.string(),
}),
DowngradesToPlan: z.object({
id: z.string(),
}),
trialRequested: z.boolean(),
})
.optional(),
nps: z.object({
id: z.string(),
startAt: z.string().datetime(),
expireAt: z.string().datetime(),
}),
banners: v.array(
v.object({
_id: v.string().required(),
_updatedAt: v.string().format('date-time').required(),
platform: v.array(v.string()).required(),
expireAt: v.string().format('date-time').required(),
startAt: v.string().format('date-time').required(),
roles: v.array(v.string()),
createdBy: v.object({
_id: v.string().required(),
username: v.string(),
banners: z.array(
z.object({
_id: z.string(),
_updatedAt: z.string().datetime(),
platform: z.array(z.string()),
expireAt: z.string().datetime(),
startAt: z.string().datetime(),
roles: z.array(z.string()).optional(),
createdBy: z.object({
_id: z.string(),
username: z.string().optional(),
}),
createdAt: v.string().format('date-time').required(),
view: v.any(),
active: v.boolean(),
inactivedAt: v.string().format('date-time'),
snapshot: v.string(),
createdAt: z.string().datetime(),
view: z.any(),
active: z.boolean().optional(),
inactivedAt: z.string().datetime().optional(),
snapshot: z.string().optional(),
}),
),
announcements: v.object({
create: v.array(
v.object({
_id: v.string().required(),
_updatedAt: v.string().format('date-time').required(),
selector: v.object({
roles: v.array(v.string()),
announcements: z.object({
create: z.array(
z.object({
_id: z.string(),
_updatedAt: z.string().datetime(),
selector: z.object({
roles: z.array(z.string()),
}),
platform: v.array(v.string().enum('web', 'mobile')).required(),
expireAt: v.string().format('date-time').required(),
startAt: v.string().format('date-time').required(),
createdBy: v.string().enum('cloud', 'system').required(),
createdAt: v.string().format('date-time').required(),
dictionary: v.object({}).additional(v.object({}).additional(v.string())),
view: v.any(),
surface: v.string().enum('banner', 'modal').required(),
platform: z.array(z.enum(['web', 'mobile'])),
expireAt: z.string().datetime(),
startAt: z.string().datetime(),
createdBy: z.enum(['cloud', 'system']),
createdAt: z.string().datetime(),
dictionary: z.record(z.record(z.string())),
view: z.any(),
surface: z.enum(['banner', 'modal']),
}),
),
delete: v.array(v.string()),
delete: z.array(z.string()),
}),
});

const assertWorkspaceClientPayload = compile(workspaceClientPayloadSchema);

/** @deprecated */
const fetchWorkspaceClientPayload = async ({
token,
Expand Down Expand Up @@ -117,7 +113,9 @@ const fetchWorkspaceClientPayload = async ({
return undefined;
}

if (!assertWorkspaceClientPayload(payload)) {
const assertWorkspaceClientPayload = workspaceClientPayloadSchema.safeParse(payload);

if (!assertWorkspaceClientPayload.success) {
throw new CloudWorkspaceConnectionError('Invalid response from Rocket.Chat Cloud');
}

Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@
"stream-buffers": "^3.0.3",
"strict-uri-encode": "^2.0.0",
"string-strip-html": "^8.5.0",
"suretype": "~2.4.1",
"swiper": "patch:swiper@npm%3A11.1.14#~/.yarn/patches/swiper-npm-11.1.14-8126fa478a.patch",
"textarea-caret": "^3.1.0",
"tinykeys": "^1.4.0",
Expand All @@ -442,7 +441,8 @@
"xml-crypto": "~3.1.0",
"xml-encryption": "~3.0.2",
"xml2js": "~0.6.2",
"yaqrcode": "^0.2.1"
"yaqrcode": "^0.2.1",
"zod": "^3.24.1"
},
"meteor": {
"mainModule": {
Expand Down
Loading
Loading