Skip to content

Commit

Permalink
Merge branch 'develop' into omnichannel/chats-onHold-action-consolida…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
kodiakhq[bot] authored Nov 1, 2023
2 parents ebe1512 + 589c451 commit fd2ff0a
Show file tree
Hide file tree
Showing 168 changed files with 3,524 additions and 2,667 deletions.
5 changes: 5 additions & 0 deletions .changeset/brown-donuts-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": minor
---

Added a new admin page called `Subscription`, this page is responsible of managing the current workspace subscription and it has a overview of the usage and limits of the plan
5 changes: 5 additions & 0 deletions .changeset/ninety-carrots-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

test: read receipts
5 changes: 5 additions & 0 deletions .changeset/workspace-status-admin-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": minor
---

Added a new Admin page called `Workspace info` in place of Information page, to make it easier to check the license
11 changes: 3 additions & 8 deletions apps/meteor/app/api/server/lib/getServerInfo.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import type { IWorkspaceInfo } from '@rocket.chat/core-typings';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import {
getCachedSupportedVersionsToken,
wrapPromise,
} from '../../../cloud/server/functions/supportedVersionsToken/supportedVersionsToken';
import { Info, minimumClientVersions } from '../../../utils/rocketchat.info';

type ServerInfo = {
info?: typeof Info;
supportedVersions?: { signed: string };
minimumClientVersions: typeof minimumClientVersions;
version: string;
};

const removePatchInfo = (version: string): string => version.replace(/(\d+\.\d+).*/, '$1');

export async function getServerInfo(userId?: string): Promise<ServerInfo> {
export async function getServerInfo(userId?: string): Promise<IWorkspaceInfo> {
const hasPermissionToViewStatistics = userId && (await hasPermissionAsync(userId, 'view-statistics'));
const supportedVersionsToken = await wrapPromise(getCachedSupportedVersionsToken());

Expand Down
50 changes: 50 additions & 0 deletions apps/meteor/app/api/server/v1/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { check } from 'meteor/check';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { hasRoleAsync } from '../../../authorization/server/functions/hasRole';
import { getCheckoutUrl } from '../../../cloud/server/functions/getCheckoutUrl';
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 { syncWorkspace } from '../../../cloud/server/functions/syncWorkspace';
import { API } from '../api';

API.v1.addRoute(
Expand Down Expand Up @@ -126,3 +128,51 @@ API.v1.addRoute(
},
},
);

API.v1.addRoute(
'cloud.syncWorkspace',
{
authRequired: true,
permissionsRequired: ['manage-cloud'],
rateLimiterOptions: { numRequestsAllowed: 2, intervalTimeInMS: 60000 },
},
{
async post() {
try {
await syncWorkspace();

return API.v1.success({ success: true });
} catch (error) {
return API.v1.failure('Error during workspace sync');
}
},
},
);

/**
* Declaring endpoint here because we don't want this available to the sdk client
*/
declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Endpoints {
'/v1/cloud.checkoutUrl': {
GET: () => { url: string };
};
}
}

API.v1.addRoute(
'cloud.checkoutUrl',
{ authRequired: true, permissionsRequired: ['manage-cloud'] },
{
async get() {
const checkoutUrl = await getCheckoutUrl();

if (!checkoutUrl.url) {
return API.v1.failure();
}

return API.v1.success({ url: checkoutUrl.url });
},
},
);
45 changes: 45 additions & 0 deletions apps/meteor/app/cloud/server/functions/getCheckoutUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { serverFetch as fetch } from '@rocket.chat/server-fetch';

import { SystemLogger } from '../../../../server/lib/logger/system';
import { settings } from '../../../settings/server';
import { getURL } from '../../../utils/server/getURL';
import { getWorkspaceAccessTokenOrThrow } from './getWorkspaceAccessToken';

export const getCheckoutUrl = async () => {
try {
const token = await getWorkspaceAccessTokenOrThrow(false, 'workspace:billing');

const subscriptionURL = getURL('admin/subscription', {
full: true,
});

const body = {
okCallback: `${subscriptionURL}?subscriptionSuccess=true`,
cancelCallback: subscriptionURL,
};

const billingUrl = settings.get<string>('Cloud_Billing_Url');

const response = await fetch(`${billingUrl}/api/v2/checkout`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
},
body,
});

if (!response.ok) {
throw new Error(await response.json());
}

return response.json();
} catch (err: any) {
SystemLogger.error({
msg: 'Failed to get Checkout URL with Rocket.Chat Billing Service',
url: '/api/v2/checkout',
err,
});

throw err;
}
};
20 changes: 12 additions & 8 deletions apps/meteor/app/cors/server/cors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,24 @@ declare module 'meteor/webapp' {
}
}

// These routes already handle cache control on their own
const cacheControlledRoutes: Array<RegExp> = ['/assets', '/custom-sounds', '/emoji-custom', '/avatar', '/file-upload'].map(
(route) => new RegExp(`^${route}`, 'i'),
);
let cachingVersion = '';
settings.watch<string>('Troubleshoot_Force_Caching_Version', (value) => {
cachingVersion = String(value).trim();
});

// @ts-expect-error - accessing internal property of webapp
WebAppInternals.staticFilesMiddleware = function (
staticFiles: StaticFiles,
req: http.IncomingMessage,
res: http.ServerResponse,
req: http.IncomingMessage & { cookies: Record<string, string> },
res: http.ServerResponse & { cookie: (cookie: string, value: string) => void },
next: NextFunction,
) {
res.setHeader('Access-Control-Allow-Origin', '*');
const { arch, path, url } = WebApp.categorizeRequest(req);

if (Meteor.isProduction && !cacheControlledRoutes.some((regexp) => regexp.test(path))) {
res.setHeader('Cache-Control', 'public, max-age=31536000');
if (cachingVersion && req.cookies.cache_version !== cachingVersion) {
res.cookie('cache_version', cachingVersion);
res.setHeader('Clear-Site-Data', '"cache"');
}

// Prevent meteor_runtime_config.js to load from a different expected hash possibly causing
Expand All @@ -126,7 +127,10 @@ WebAppInternals.staticFilesMiddleware = function (
res.writeHead(404);
return res.end();
}

res.setHeader('Cache-Control', 'public, max-age=3600');
}

return _staticFilesMiddleware(staticFiles, req, res, next);
};

Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/api/lib/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export function findGuest(token: string): Promise<ILivechatVisitor | null> {
token: 1,
visitorEmails: 1,
department: 1,
activity: 1,
},
});
}
Expand Down
11 changes: 9 additions & 2 deletions apps/meteor/app/livechat/server/hooks/markRoomResponded.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IOmnichannelRoom } from '@rocket.chat/core-typings';
import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings';
import { LivechatRooms, LivechatVisitors } from '@rocket.chat/models';
import { LivechatRooms, LivechatVisitors, LivechatInquiry } from '@rocket.chat/models';
import moment from 'moment';

import { callbacks } from '../../../../lib/callbacks';
Expand Down Expand Up @@ -30,11 +30,18 @@ callbacks.add(
// Return YYYY-MM from moment
const monthYear = moment().format('YYYY-MM');
const isVisitorActive = await LivechatVisitors.isVisitorActiveOnPeriod(room.v._id, monthYear);

// Case: agent answers & visitor is not active, we mark visitor as active
if (!isVisitorActive) {
await LivechatVisitors.markVisitorActiveForPeriod(room.v._id, monthYear);
}

await LivechatRooms.markVisitorActiveForPeriod(room._id, monthYear);
if (!room.v?.activity?.includes(monthYear)) {
await Promise.all([
LivechatRooms.markVisitorActiveForPeriod(room._id, monthYear),
LivechatInquiry.markInquiryActiveForPeriod(room._id, monthYear),
]);
}

if (room.responseBy) {
await LivechatRooms.setAgentLastMessageTs(room._id);
Expand Down
18 changes: 14 additions & 4 deletions apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ export const createLivechatRoom = async (
const { _id, username, token, department: departmentId, status = 'online' } = guest;
const newRoomAt = new Date();

logger.debug(`Creating livechat room for visitor ${_id}`);
const { activity } = guest;
logger.debug({
msg: `Creating livechat room for visitor ${_id}`,
visitor: { _id, username, departmentId, status, activity },
});

const room: InsertionModel<IOmnichannelRoom> = Object.assign(
{
Expand All @@ -94,6 +98,7 @@ export const createLivechatRoom = async (
username,
token,
status,
...(activity?.length && { activity }),
},
cl: false,
open: true,
Expand Down Expand Up @@ -132,7 +137,7 @@ export const createLivechatInquiry = async ({
}: {
rid: string;
name?: string;
guest?: Pick<ILivechatVisitor, '_id' | 'username' | 'status' | 'department' | 'name' | 'token'>;
guest?: Pick<ILivechatVisitor, '_id' | 'username' | 'status' | 'department' | 'name' | 'token' | 'activity'>;
message?: Pick<IMessage, 'msg'>;
initialStatus?: LivechatInquiryStatus;
extraData?: Pick<ILivechatInquiryRecord, 'source'>;
Expand All @@ -146,6 +151,7 @@ export const createLivechatInquiry = async ({
username: String,
status: Match.Maybe(String),
department: Match.Maybe(String),
activity: Match.Maybe([String]),
}),
);
check(
Expand All @@ -157,11 +163,14 @@ export const createLivechatInquiry = async ({

const extraInquiryInfo = await callbacks.run('livechat.beforeInquiry', extraData);

const { _id, username, token, department, status = UserStatus.ONLINE } = guest;
const { _id, username, token, department, status = UserStatus.ONLINE, activity } = guest;
const { msg } = message;
const ts = new Date();

logger.debug(`Creating livechat inquiry for visitor ${_id}`);
logger.debug({
msg: `Creating livechat inquiry for visitor ${_id}`,
visitor: { _id, username, department, status, activity },
});

const inquiry: InsertionModel<ILivechatInquiryRecord> = {
rid,
Expand All @@ -175,6 +184,7 @@ export const createLivechatInquiry = async ({
username,
token,
status,
...(activity?.length && { activity }),
},
t: 'l',
priorityWeight: LivechatPriorityWeight.NOT_SPECIFIED,
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/lib/QueueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const QueueManager: queueManager = {
status: Match.Maybe(String),
department: Match.Maybe(String),
name: Match.Maybe(String),
activity: Match.Maybe([String]),
}),
);

Expand Down
33 changes: 23 additions & 10 deletions apps/meteor/app/statistics/server/lib/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,18 +305,30 @@ export const statistics = {
);

// Message statistics
statistics.totalChannelMessages = (await Rooms.findByType('c', { projection: { msgs: 1 } }).toArray()).reduce(
function _countChannelMessages(num: number, room: IRoom) {
const channels = await Rooms.findByType('c', { projection: { msgs: 1, prid: 1 } }).toArray();
const totalChannelDiscussionsMessages = await channels.reduce(function _countChannelDiscussionsMessages(num: number, room: IRoom) {
return num + (room.prid ? room.msgs : 0);
}, 0);
statistics.totalChannelMessages =
(await channels.reduce(function _countChannelMessages(num: number, room: IRoom) {
return num + room.msgs;
},
0,
);
statistics.totalPrivateGroupMessages = (await Rooms.findByType('p', { projection: { msgs: 1 } }).toArray()).reduce(
function _countPrivateGroupMessages(num: number, room: IRoom) {
}, 0)) - totalChannelDiscussionsMessages;

const privateGroups = await Rooms.findByType('p', { projection: { msgs: 1, prid: 1 } }).toArray();
const totalPrivateGroupsDiscussionsMessages = await privateGroups.reduce(function _countPrivateGroupsDiscussionsMessages(
num: number,
room: IRoom,
) {
return num + (room.prid ? room.msgs : 0);
},
0);
statistics.totalPrivateGroupMessages =
(await privateGroups.reduce(function _countPrivateGroupMessages(num: number, room: IRoom) {
return num + room.msgs;
},
0,
);
}, 0)) - totalPrivateGroupsDiscussionsMessages;

statistics.totalDiscussionsMessages = totalPrivateGroupsDiscussionsMessages + totalChannelDiscussionsMessages;

statistics.totalDirectMessages = (await Rooms.findByType('d', { projection: { msgs: 1 } }).toArray()).reduce(
function _countDirectMessages(num: number, room: IRoom) {
return num + room.msgs;
Expand All @@ -332,6 +344,7 @@ export const statistics = {
statistics.totalMessages =
statistics.totalChannelMessages +
statistics.totalPrivateGroupMessages +
statistics.totalDiscussionsMessages +
statistics.totalDirectMessages +
statistics.totalLivechatMessages;

Expand Down
14 changes: 13 additions & 1 deletion apps/meteor/app/utils/lib/getURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,19 @@ export const _getURL = (
export const getURLWithoutSettings = (
path: string,
// eslint-disable-next-line @typescript-eslint/naming-convention
{ cdn = true, full = false, cloud = false, cloud_route = '', cloud_params = {} }: Record<string, any> = {},
{
cdn = true,
full = false,
cloud = false,
cloud_route = '',
cloud_params = {},
}: {
cdn?: boolean;
full?: boolean;
cloud?: boolean;
cloud_route?: string;
cloud_params?: Record<string, string>;
},
cdnPrefix: string,
siteUrl: string,
cloudDeepLinkUrl?: string,
Expand Down
8 changes: 7 additions & 1 deletion apps/meteor/app/utils/server/getURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { getURLWithoutSettings } from '../lib/getURL';

export const getURL = function (
path: string, // eslint-disable-next-line @typescript-eslint/naming-convention
params: Record<string, any> = {},
params: {
cdn?: boolean;
full?: boolean;
cloud?: boolean;
cloud_route?: string;
cloud_params?: Record<string, string>;
} = {},
cloudDeepLinkUrl?: string,
): string {
const cdnPrefix = settings.get<string>('CDN_PREFIX') || '';
Expand Down
Loading

0 comments on commit fd2ff0a

Please sign in to comment.