Skip to content

Commit

Permalink
Merge branch 'develop' into feat/license-fair-policy
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Oct 9, 2023
2 parents a467e4a + 06a8e30 commit 0b831e5
Show file tree
Hide file tree
Showing 36 changed files with 304 additions and 170 deletions.
5 changes: 5 additions & 0 deletions .changeset/odd-hounds-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

chore: Change plan name Enterprise to Premium on marketplace
5 changes: 5 additions & 0 deletions .changeset/seven-carpets-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Add new permission to allow kick users from rooms without being a member
5 changes: 5 additions & 0 deletions .changeset/sweet-feet-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

fix: user dropdown menu position on RTL layout
5 changes: 5 additions & 0 deletions .changeset/wicked-humans-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Improve cache of static files
74 changes: 40 additions & 34 deletions apps/meteor/app/api/server/v1/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,7 @@ import { getLoggedInUser } from '../helpers/getLoggedInUser';
import { getPaginationItems } from '../helpers/getPaginationItems';
import { getUserFromParams, getUserListFromParams } from '../helpers/getUserFromParams';

// 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({
params,
checkedArchived = true,
userId,
}: {
params:
| {
roomId?: string;
}
| {
roomName?: string;
};
userId: string;
checkedArchived?: boolean;
}): Promise<{
rid: string;
open: boolean;
ro: boolean;
t: string;
name: string;
broadcast: boolean;
}> {
async function getRoomFromParams(params: { roomId?: string } | { roomName?: string }): Promise<IRoom> {
if (
(!('roomId' in params) && !('roomName' in params)) ||
('roomId' in params && !(params as { roomId?: string }).roomId && 'roomName' in params && !(params as { roomName?: string }).roomName)
Expand All @@ -68,17 +46,48 @@ async function findPrivateGroupByIdOrName({
broadcast: 1,
},
};
let room: IRoom | null = null;
if ('roomId' in params) {
room = await Rooms.findOneById(params.roomId || '', roomOptions);
} else if ('roomName' in params) {
room = await Rooms.findOneByName(params.roomName || '', roomOptions);
}

const room = await (() => {
if ('roomId' in params) {
return Rooms.findOneById(params.roomId || '', roomOptions);
}
if ('roomName' in params) {
return Rooms.findOneByName(params.roomName || '', roomOptions);
}
})();

if (!room || room.t !== 'p') {
throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group');
}

return room;
}

// 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({
params,
checkedArchived = true,
userId,
}: {
params:
| {
roomId?: string;
}
| {
roomName?: string;
};
userId: string;
checkedArchived?: boolean;
}): Promise<{
rid: string;
open: boolean;
ro: boolean;
t: string;
name: string;
broadcast: boolean;
}> {
const room = await getRoomFromParams(params);

const user = await Users.findOneById(userId, { projections: { username: 1 } });

if (!room || !user || !(await canAccessRoomAsync(room, user))) {
Expand Down Expand Up @@ -585,17 +594,14 @@ API.v1.addRoute(
{ authRequired: true },
{
async post() {
const findResult = await findPrivateGroupByIdOrName({
params: this.bodyParams,
userId: this.userId,
});
const room = await getRoomFromParams(this.bodyParams);

const user = await getUserFromParams(this.bodyParams);
if (!user?.username) {
return API.v1.failure('Invalid user');
}

await removeUserFromRoomMethod(this.userId, { rid: findResult.rid, username: user.username });
await removeUserFromRoomMethod(this.userId, { rid: room._id, username: user.username });

return API.v1.success();
},
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/app/authorization/server/constant/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const permissions = [
{ _id: 'add-user-to-joined-room', roles: ['admin', 'owner', 'moderator'] },
{ _id: 'add-user-to-any-c-room', roles: ['admin'] },
{ _id: 'add-user-to-any-p-room', roles: [] },
{ _id: 'kick-user-from-any-c-room', roles: ['admin'] },
{ _id: 'kick-user-from-any-p-room', roles: [] },
{ _id: 'api-bypass-rate-limit', roles: ['admin', 'bot', 'app'] },
{ _id: 'archive-room', roles: ['admin', 'owner'] },
{ _id: 'assign-admin-role', roles: ['admin'] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function buildWorkspaceRegistrationData<T extends string | undefine
setupComplete: setupWizardState === 'completed',
connectionDisable: !registerServer,
npsEnabled,
MAC: stats.omnichannelContactsBySource.contactsCount,
MAC: stats.omnichannelContactsBySource?.contactsCount ?? 0,
// activeContactsBillingMonth: stats.omnichannelContactsBySource.contactsCount,
// activeContactsYesterday: stats.uniqueContactsOfYesterday.contactsCount,
};
Expand Down
41 changes: 41 additions & 0 deletions apps/meteor/app/cors/server/cors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { createHash } from 'crypto';
import type http from 'http';
import type { UrlWithParsedQuery } from 'url';
import url from 'url';

import { Logger } from '@rocket.chat/logger';
Expand Down Expand Up @@ -77,6 +79,19 @@ WebApp.rawConnectHandlers.use((_req: http.IncomingMessage, res: http.ServerRespo
});

const _staticFilesMiddleware = WebAppInternals.staticFilesMiddleware;
declare module 'meteor/webapp' {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace WebApp {
function categorizeRequest(
req: http.IncomingMessage,
): { arch: string; path: string; url: UrlWithParsedQuery } & Record<string, unknown>;
}
}

// 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'),
);

// @ts-expect-error - accessing internal property of webapp
WebAppInternals.staticFilesMiddleware = function (
Expand All @@ -86,6 +101,32 @@ WebAppInternals.staticFilesMiddleware = function (
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');
}

// Prevent meteor_runtime_config.js to load from a different expected hash possibly causing
// a cache of the file for the wrong hash and start a client loop due to the mismatch
// of the hashes of ui versions which would be checked against a websocket response
if (path === '/meteor_runtime_config.js') {
const program = WebApp.clientPrograms[arch] as (typeof WebApp.clientPrograms)[string] & {
meteorRuntimeConfigHash?: string;
meteorRuntimeConfig: string;
};

if (!program?.meteorRuntimeConfigHash) {
program.meteorRuntimeConfigHash = createHash('sha1')
.update(JSON.stringify(encodeURIComponent(program.meteorRuntimeConfig)))
.digest('hex');
}

if (program.meteorRuntimeConfigHash !== url.query.hash) {
res.writeHead(404);
return res.end();
}
}
return _staticFilesMiddleware(staticFiles, req, res, next);
};

Expand Down
29 changes: 15 additions & 14 deletions apps/meteor/app/custom-sounds/client/lib/CustomSounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ import { getURL } from '../../../utils/client';
import { sdk } from '../../../utils/client/lib/SDKClient';

const getCustomSoundId = (soundId: ICustomSound['_id']) => `custom-sound-${soundId}`;
const getAssetUrl = (asset: string, params?: Record<string, any>) => getURL(asset, params, undefined, true);

const defaultSounds = [
{ _id: 'chime', name: 'Chime', extension: 'mp3', src: getURL('sounds/chime.mp3') },
{ _id: 'door', name: 'Door', extension: 'mp3', src: getURL('sounds/door.mp3') },
{ _id: 'beep', name: 'Beep', extension: 'mp3', src: getURL('sounds/beep.mp3') },
{ _id: 'chelle', name: 'Chelle', extension: 'mp3', src: getURL('sounds/chelle.mp3') },
{ _id: 'ding', name: 'Ding', extension: 'mp3', src: getURL('sounds/ding.mp3') },
{ _id: 'droplet', name: 'Droplet', extension: 'mp3', src: getURL('sounds/droplet.mp3') },
{ _id: 'highbell', name: 'Highbell', extension: 'mp3', src: getURL('sounds/highbell.mp3') },
{ _id: 'seasons', name: 'Seasons', extension: 'mp3', src: getURL('sounds/seasons.mp3') },
{ _id: 'telephone', name: 'Telephone', extension: 'mp3', src: getURL('sounds/telephone.mp3') },
{ _id: 'outbound-call-ringing', name: 'Outbound Call Ringing', extension: 'mp3', src: getURL('sounds/outbound-call-ringing.mp3') },
{ _id: 'call-ended', name: 'Call Ended', extension: 'mp3', src: getURL('sounds/call-ended.mp3') },
{ _id: 'dialtone', name: 'Dialtone', extension: 'mp3', src: getURL('sounds/dialtone.mp3') },
{ _id: 'ringtone', name: 'Ringtone', extension: 'mp3', src: getURL('sounds/ringtone.mp3') },
{ _id: 'chime', name: 'Chime', extension: 'mp3', src: getAssetUrl('sounds/chime.mp3') },
{ _id: 'door', name: 'Door', extension: 'mp3', src: getAssetUrl('sounds/door.mp3') },
{ _id: 'beep', name: 'Beep', extension: 'mp3', src: getAssetUrl('sounds/beep.mp3') },
{ _id: 'chelle', name: 'Chelle', extension: 'mp3', src: getAssetUrl('sounds/chelle.mp3') },
{ _id: 'ding', name: 'Ding', extension: 'mp3', src: getAssetUrl('sounds/ding.mp3') },
{ _id: 'droplet', name: 'Droplet', extension: 'mp3', src: getAssetUrl('sounds/droplet.mp3') },
{ _id: 'highbell', name: 'Highbell', extension: 'mp3', src: getAssetUrl('sounds/highbell.mp3') },
{ _id: 'seasons', name: 'Seasons', extension: 'mp3', src: getAssetUrl('sounds/seasons.mp3') },
{ _id: 'telephone', name: 'Telephone', extension: 'mp3', src: getAssetUrl('sounds/telephone.mp3') },
{ _id: 'outbound-call-ringing', name: 'Outbound Call Ringing', extension: 'mp3', src: getAssetUrl('sounds/outbound-call-ringing.mp3') },
{ _id: 'call-ended', name: 'Call Ended', extension: 'mp3', src: getAssetUrl('sounds/call-ended.mp3') },
{ _id: 'dialtone', name: 'Dialtone', extension: 'mp3', src: getAssetUrl('sounds/dialtone.mp3') },
{ _id: 'ringtone', name: 'Ringtone', extension: 'mp3', src: getAssetUrl('sounds/ringtone.mp3') },
];

class CustomSoundsClass {
Expand Down Expand Up @@ -85,7 +86,7 @@ class CustomSoundsClass {
}

getURL(sound: ICustomSound) {
return getURL(`/custom-sounds/${sound._id}.${sound.extension}?_dc=${sound.random || 0}`);
return getAssetUrl(`/custom-sounds/${sound._id}.${sound.extension}`, { _dc: sound.random || 0 });
}

getList() {
Expand Down
24 changes: 17 additions & 7 deletions apps/meteor/app/ui-master/server/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const callback: NextHandleFunction = (req, res, next) => {
return;
}

const injection = headInjections.get(pathname.replace(/^\//, '')) as Injection | undefined;
const injection = headInjections.get(pathname.replace(/^\//, '').split('_')[0]) as Injection | undefined;

if (!injection || typeof injection === 'string') {
next();
Expand Down Expand Up @@ -76,27 +76,37 @@ export const injectIntoHead = (key: string, value: Injection): void => {
};

export const addScript = (key: string, content: string): void => {
if (/_/.test(key)) {
throw new Error('inject.js > addScript - key cannot contain "_" (underscore)');
}

if (!content.trim()) {
injectIntoHead(`${key}.js`, '');
injectIntoHead(key, '');
return;
}
const currentHash = crypto.createHash('sha1').update(content).digest('hex');
injectIntoHead(`${key}.js`, {

injectIntoHead(key, {
type: 'JS',
tag: `<script id="${key}" type="text/javascript" src="${`${getURL(key)}.js?${currentHash}`}"></script>`,
tag: `<script id="${key}" type="text/javascript" src="${`${getURL(key)}_${currentHash}.js`}"></script>`,
content,
});
};

export const addStyle = (key: string, content: string): void => {
if (/_/.test(key)) {
throw new Error('inject.js > addStyle - key cannot contain "_" (underscore)');
}

if (!content.trim()) {
injectIntoHead(`${key}.css`, '');
injectIntoHead(key, '');
return;
}
const currentHash = crypto.createHash('sha1').update(content).digest('hex');
injectIntoHead(`${key}.css`, {

injectIntoHead(key, {
type: 'CSS',
tag: `<link id="${key}" rel="stylesheet" type="text/css" href="${`${getURL(key)}.css?${currentHash}`}">`,
tag: `<link id="${key}" rel="stylesheet" type="text/css" href="${`${getURL(key)}_${currentHash}.css`}">`,
content,
});
};
Expand Down
6 changes: 6 additions & 0 deletions apps/meteor/app/utils/client/getURL.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { settings } from '../../settings/client';
import { getURLWithoutSettings } from '../lib/getURL';
import { Info } from '../rocketchat.info';

export const getURL = function (
path: string, // eslint-disable-next-line @typescript-eslint/naming-convention
params: Record<string, any> = {},
cloudDeepLinkUrl?: string,
cacheKey?: boolean,
): string {
const cdnPrefix = settings.get('CDN_PREFIX') || '';
const siteUrl = settings.get('Site_Url') || '';

if (cacheKey) {
path += `${path.includes('?') ? '&' : '?'}cacheKey=${Info.version}`;
}

return getURLWithoutSettings(path, params, cdnPrefix, siteUrl, cloudDeepLinkUrl);
};
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const GenericUpsellModal = ({
<Modal.Header>
{icon && <Modal.Icon name={icon} />}
<Modal.HeaderText>
<Modal.Tagline color='font-annotation'>{tagline ?? t('Enterprise_capability')}</Modal.Tagline>
<Modal.Tagline color='font-annotation'>{tagline ?? t('Premium_capability')}</Modal.Tagline>
<Modal.Title>{title}</Modal.Title>
</Modal.HeaderText>
<Modal.Close aria-label={t('Close')} onClick={onClose} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const MessageContentBody = ({ mentions, channels, md, searchText }: MessageConte
text-decoration: underline;
}
&:focus {
border: 2px solid ${Palette.stroke['stroke-extra-light-highlight']};
box-shadow: 0 0 0 2px ${Palette.stroke['stroke-extra-light-highlight']};
border-radius: 2px;
}
}
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/client/sidebar/header/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const UserMenu = ({ user }: { user: IUser }) => {
<FeaturePreviewOff>
<GenericMenu
icon={<UserAvatarWithStatus />}
placement='bottom-end'
selectionMode='multiple'
sections={sections}
title={t('User_menu')}
Expand All @@ -36,6 +37,7 @@ const UserMenu = ({ user }: { user: IUser }) => {
<GenericMenu
icon={<UserAvatarWithStatusUnstable />}
medium
placement='bottom-end'
selectionMode='multiple'
sections={sections}
title={t('User_menu')}
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/views/admin/rooms/EditRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement =>
</Box>
)}
<Field>
<FieldLabel>{t('Name')}</FieldLabel>
<FieldLabel required>{t('Name')}</FieldLabel>
<FieldRow>
<TextInput disabled={deleting || !canViewName} value={roomName} onChange={handleRoomName} flexGrow={1} />
</FieldRow>
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/client/views/admin/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ declare module '@rocket.chat/ui-contexts' {
pattern: '/admin/registration/:page?';
};
'admin-view-logs': {
pathname: '/admin/records';
pattern: '/admin/records';
pathname: '/admin/reports';
pattern: '/admin/reports';
};
'federation-dashboard': {
pathname: '/admin/federation';
Expand Down Expand Up @@ -193,7 +193,7 @@ registerAdminRoute('/registration/:page?', {
component: lazy(() => import('./cloud/CloudRoute')),
});

registerAdminRoute('/records', {
registerAdminRoute('/reports', {
name: 'admin-view-logs',
component: lazy(() => import('./viewLogs/ViewLogsRoute')),
});
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/client/views/admin/sidebarItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ export const {
permissionGranted: (): boolean => hasPermission('run-import'),
},
{
href: '/admin/records',
i18nLabel: 'Records',
href: '/admin/reports',
i18nLabel: 'Reports',
icon: 'post',
permissionGranted: (): boolean => hasPermission('view-logs'),
},
Expand Down
Loading

0 comments on commit 0b831e5

Please sign in to comment.