diff --git a/apps/meteor/app/apps/server/bridges/activation.ts b/apps/meteor/app/apps/server/bridges/activation.ts index 511d3136dceb..dce9d0011ec2 100644 --- a/apps/meteor/app/apps/server/bridges/activation.ts +++ b/apps/meteor/app/apps/server/bridges/activation.ts @@ -3,7 +3,7 @@ import type { ProxiedApp } from '@rocket.chat/apps-engine/server/ProxiedApp'; import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import { Users } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppActivationBridge extends ActivationBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/api.ts b/apps/meteor/app/apps/server/bridges/api.ts index d60a120dcfbb..2d18cb4bc64f 100644 --- a/apps/meteor/app/apps/server/bridges/api.ts +++ b/apps/meteor/app/apps/server/bridges/api.ts @@ -7,7 +7,7 @@ import type { IApiRequest, IApiEndpoint, IApi } from '@rocket.chat/apps-engine/d 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 '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { authenticationMiddleware } from '../../../api/server/middlewares/authentication'; const apiServer = express(); @@ -55,7 +55,7 @@ export class AppApisBridge extends ApiBridge { }); } - public registerApi({ api, computedPath, endpoint }: AppApi, appId: string): void { + public async registerApi({ api, computedPath, endpoint }: AppApi, appId: string): Promise { this.orch.debugLog(`The App ${appId} is registering the api: "${endpoint.path}" (${computedPath})`); this._verifyApi(api, endpoint); @@ -79,7 +79,7 @@ export class AppApisBridge extends ApiBridge { } } - public unregisterApis(appId: string): void { + public async unregisterApis(appId: string): Promise { this.orch.debugLog(`The App ${appId} is unregistering all apis`); if (this.appRouters.get(appId)) { diff --git a/apps/meteor/app/apps/server/bridges/bridges.js b/apps/meteor/app/apps/server/bridges/bridges.js index 67d067496ce9..9e31d3f2f51b 100644 --- a/apps/meteor/app/apps/server/bridges/bridges.js +++ b/apps/meteor/app/apps/server/bridges/bridges.js @@ -20,6 +20,7 @@ import { UiInteractionBridge } from './uiInteraction'; import { AppSchedulerBridge } from './scheduler'; import { AppVideoConferenceBridge } from './videoConferences'; import { AppOAuthAppsBridge } from './oauthApps'; +import { AppInternalFederationBridge } from './internalFederation'; export class RealAppBridges extends AppBridges { constructor(orch) { @@ -45,6 +46,7 @@ export class RealAppBridges extends AppBridges { this._cloudWorkspaceBridge = new AppCloudBridge(orch); this._videoConfBridge = new AppVideoConferenceBridge(orch); this._oAuthBridge = new AppOAuthAppsBridge(orch); + this._internalFedBridge = new AppInternalFederationBridge(orch); } getCommandBridge() { @@ -126,4 +128,8 @@ export class RealAppBridges extends AppBridges { getOAuthAppsBridge() { return this._oAuthBridge; } + + getInternalFederationBridge() { + return this._internalFedBridge; + } } diff --git a/apps/meteor/app/apps/server/bridges/cloud.ts b/apps/meteor/app/apps/server/bridges/cloud.ts index 6f3569af0579..87b94de7ad7f 100644 --- a/apps/meteor/app/apps/server/bridges/cloud.ts +++ b/apps/meteor/app/apps/server/bridges/cloud.ts @@ -3,7 +3,7 @@ import { CloudWorkspaceBridge } from '@rocket.chat/apps-engine/server/bridges/Cl import type { IWorkspaceToken } from '@rocket.chat/apps-engine/definition/cloud/IWorkspaceToken'; import { getWorkspaceAccessTokenWithScope } from '../../../cloud/server'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; const boundGetWorkspaceAccessToken = Meteor.bindEnvironment(getWorkspaceAccessTokenWithScope); diff --git a/apps/meteor/app/apps/server/bridges/commands.ts b/apps/meteor/app/apps/server/bridges/commands.ts index 42735ec762ae..b4908fafe986 100644 --- a/apps/meteor/app/apps/server/bridges/commands.ts +++ b/apps/meteor/app/apps/server/bridges/commands.ts @@ -5,8 +5,8 @@ import { CommandBridge } from '@rocket.chat/apps-engine/server/bridges/CommandBr import type { IMessage, RequiredField, SlashCommand } from '@rocket.chat/core-typings'; import { slashCommands } from '../../../utils/server'; -import { Utilities } from '../../lib/misc/Utilities'; -import type { AppServerOrchestrator } from '../orchestrator'; +import { Utilities } from '../../../../ee/lib/misc/Utilities'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { parseParameters } from '../../../../lib/utils/parseParameters'; export class AppCommandsBridge extends CommandBridge { @@ -18,7 +18,7 @@ export class AppCommandsBridge extends CommandBridge { this.disabledCommands = new Map(); } - protected doesCommandExist(command: string, appId: string): boolean { + protected async doesCommandExist(command: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is checking if "${command}" command exists.`); if (typeof command !== 'string' || command.length === 0) { @@ -30,7 +30,7 @@ export class AppCommandsBridge extends CommandBridge { return typeof slashCommands.commands[cmd] === 'object' || this.disabledCommands.has(cmd); } - protected enableCommand(command: string, appId: string): void { + protected async enableCommand(command: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is attempting to enable the command: "${command}"`); if (typeof command !== 'string' || command.trim().length === 0) { @@ -48,7 +48,7 @@ export class AppCommandsBridge extends CommandBridge { this.orch.getNotifier().commandUpdated(cmd); } - protected disableCommand(command: string, appId: string): void { + protected async disableCommand(command: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is attempting to disable the command: "${command}"`); if (typeof command !== 'string' || command.trim().length === 0) { @@ -74,7 +74,7 @@ export class AppCommandsBridge extends CommandBridge { } // command: { command, paramsExample, i18nDescription, executor: function } - protected modifyCommand(command: ISlashCommand, appId: string): void { + protected async modifyCommand(command: ISlashCommand, appId: string): Promise { this.orch.debugLog(`The App ${appId} is attempting to modify the command: "${command}"`); this._verifyCommand(command); @@ -99,7 +99,7 @@ export class AppCommandsBridge extends CommandBridge { this.orch.getNotifier().commandUpdated(cmd); } - protected registerCommand(command: ISlashCommand, appId: string): void { + protected async registerCommand(command: ISlashCommand, appId: string): Promise { this.orch.debugLog(`The App ${appId} is registering the command: "${command.command}"`); this._verifyCommand(command); @@ -122,7 +122,7 @@ export class AppCommandsBridge extends CommandBridge { this.orch.getNotifier().commandAdded(command.command.toLowerCase()); } - protected unregisterCommand(command: string, appId: string): void { + protected async unregisterCommand(command: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is unregistering the command: "${command}"`); if (typeof command !== 'string' || command.trim().length === 0) { diff --git a/apps/meteor/app/apps/server/bridges/details.ts b/apps/meteor/app/apps/server/bridges/details.ts index 1cea999fd3c8..cb60cc56f3aa 100644 --- a/apps/meteor/app/apps/server/bridges/details.ts +++ b/apps/meteor/app/apps/server/bridges/details.ts @@ -1,7 +1,7 @@ import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import { AppDetailChangesBridge as DetailChangesBridge } from '@rocket.chat/apps-engine/server/bridges/AppDetailChangesBridge'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppDetailChangesBridge extends DetailChangesBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/environmental.ts b/apps/meteor/app/apps/server/bridges/environmental.ts index 2f7a46ae7934..e159ad277998 100644 --- a/apps/meteor/app/apps/server/bridges/environmental.ts +++ b/apps/meteor/app/apps/server/bridges/environmental.ts @@ -1,6 +1,6 @@ import { EnvironmentalVariableBridge } from '@rocket.chat/apps-engine/server/bridges/EnvironmentalVariableBridge'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppEnvironmentalVariableBridge extends EnvironmentalVariableBridge { allowed: Array; diff --git a/apps/meteor/app/apps/server/bridges/http.ts b/apps/meteor/app/apps/server/bridges/http.ts index c67a1b48470c..264c6923281d 100644 --- a/apps/meteor/app/apps/server/bridges/http.ts +++ b/apps/meteor/app/apps/server/bridges/http.ts @@ -2,7 +2,7 @@ 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 type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { fetch } from '../../../../server/lib/http/fetch'; const isGetOrHead = (method: string): boolean => ['GET', 'HEAD'].includes(method.toUpperCase()); diff --git a/apps/meteor/app/apps/server/bridges/internal.ts b/apps/meteor/app/apps/server/bridges/internal.ts index 8355ec817d64..6584a7ac5e8b 100644 --- a/apps/meteor/app/apps/server/bridges/internal.ts +++ b/apps/meteor/app/apps/server/bridges/internal.ts @@ -3,7 +3,7 @@ import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import type { ISubscription } from '@rocket.chat/core-typings'; import { Settings } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { Subscriptions } from '../../../models/server'; export class AppInternalBridge extends InternalBridge { diff --git a/apps/meteor/app/apps/server/bridges/internalFederation.ts b/apps/meteor/app/apps/server/bridges/internalFederation.ts new file mode 100644 index 000000000000..9bf3b34439ce --- /dev/null +++ b/apps/meteor/app/apps/server/bridges/internalFederation.ts @@ -0,0 +1,12 @@ +import type { IInternalFederationBridge } from '@rocket.chat/apps-engine/server/bridges/IInternalFederationBridge'; +import { FederationKeys } from '@rocket.chat/models'; + +export class AppInternalFederationBridge implements IInternalFederationBridge { + async getPrivateKey(): Promise { + return FederationKeys.getKey('private'); + } + + async getPublicKey(): Promise { + return FederationKeys.getKey('public'); + } +} diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index ba84a1f77e6e..9b514097c9ad 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -16,7 +16,7 @@ import { LivechatVisitors } from '@rocket.chat/models'; import { getRoom } from '../../../livechat/server/api/lib/livechat'; import { Livechat } from '../../../livechat/server/lib/Livechat'; import { Users, LivechatDepartment, LivechatRooms } from '../../../models/server'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped'; export class AppLivechatBridge extends LivechatBridge { diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index 4f905044b65c..b90e590bfca8 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -10,7 +10,7 @@ import { Messages, Users, Subscriptions } from '../../../models/server'; 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 '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppMessageBridge extends MessageBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/oauthApps.ts b/apps/meteor/app/apps/server/bridges/oauthApps.ts index e29443d8cc49..6d2c25ba2e25 100644 --- a/apps/meteor/app/apps/server/bridges/oauthApps.ts +++ b/apps/meteor/app/apps/server/bridges/oauthApps.ts @@ -5,7 +5,7 @@ import { OAuthApps, Users } from '@rocket.chat/models'; import { Random } from 'meteor/random'; import { v4 as uuidv4 } from 'uuid'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppOAuthAppsBridge extends OAuthAppsBridge { constructor(private readonly orch: AppServerOrchestrator) { diff --git a/apps/meteor/app/apps/server/bridges/persistence.ts b/apps/meteor/app/apps/server/bridges/persistence.ts index b14aef68eb3f..b1b8f0b5a642 100644 --- a/apps/meteor/app/apps/server/bridges/persistence.ts +++ b/apps/meteor/app/apps/server/bridges/persistence.ts @@ -1,7 +1,7 @@ import { PersistenceBridge } from '@rocket.chat/apps-engine/server/bridges/PersistenceBridge'; import type { RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppPersistenceBridge extends PersistenceBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index 591a8b9465b2..d8d45faac097 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -6,7 +6,7 @@ import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import { Meteor } from 'meteor/meteor'; import type { ISubscription, IUser as ICoreUser } from '@rocket.chat/core-typings'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { Rooms, Subscriptions, Users } from '../../../models/server'; import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; diff --git a/apps/meteor/app/apps/server/bridges/scheduler.ts b/apps/meteor/app/apps/server/bridges/scheduler.ts index b4ab5170fc4f..cf22b52e8cb5 100644 --- a/apps/meteor/app/apps/server/bridges/scheduler.ts +++ b/apps/meteor/app/apps/server/bridges/scheduler.ts @@ -6,7 +6,7 @@ import type { IProcessor, IOnetimeSchedule, IRecurringSchedule, IJobContext } fr import { StartupType } from '@rocket.chat/apps-engine/definition/scheduler'; import { SchedulerBridge } from '@rocket.chat/apps-engine/server/bridges/SchedulerBridge'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; function _callProcessor(processor: IProcessor['processor']): (job: Job) => Promise { return (job) => { diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index 7be7a888043d..14e4834c3632 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -2,7 +2,7 @@ import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import { ServerSettingBridge } from '@rocket.chat/apps-engine/server/bridges/ServerSettingBridge'; import { Settings } from '@rocket.chat/models'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppSettingBridge extends ServerSettingBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/uiInteraction.ts b/apps/meteor/app/apps/server/bridges/uiInteraction.ts index ad30dc575821..6db79eacd984 100644 --- a/apps/meteor/app/apps/server/bridges/uiInteraction.ts +++ b/apps/meteor/app/apps/server/bridges/uiInteraction.ts @@ -3,7 +3,7 @@ import type { IUIKitInteraction } from '@rocket.chat/apps-engine/definition/uiki import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import { api } from '@rocket.chat/core-services'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class UiInteractionBridge extends UiIntBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/uploads.ts b/apps/meteor/app/apps/server/bridges/uploads.ts index 21de997be2f6..94655eadef02 100644 --- a/apps/meteor/app/apps/server/bridges/uploads.ts +++ b/apps/meteor/app/apps/server/bridges/uploads.ts @@ -4,8 +4,8 @@ import type { IUpload } from '@rocket.chat/apps-engine/definition/uploads'; import type { IUploadDetails } from '@rocket.chat/apps-engine/definition/uploads/IUploadDetails'; import { FileUpload } from '../../../file-upload/server'; -import { determineFileType } from '../../lib/misc/determineFileType'; -import type { AppServerOrchestrator } from '../orchestrator'; +import { determineFileType } from '../../../../ee/lib/misc/determineFileType'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; const getUploadDetails = (details: IUploadDetails): Partial => { if (details.visitorToken) { diff --git a/apps/meteor/app/apps/server/bridges/users.ts b/apps/meteor/app/apps/server/bridges/users.ts index 9185b567b1cd..1b95601146b9 100644 --- a/apps/meteor/app/apps/server/bridges/users.ts +++ b/apps/meteor/app/apps/server/bridges/users.ts @@ -5,7 +5,7 @@ import { Subscriptions, Users as UsersRaw } from '@rocket.chat/models'; import { setUserAvatar, checkUsernameAvailability, deleteUser, getUserCreatedByApp } from '../../../lib/server/functions'; import { Users } from '../../../models/server'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; export class AppUserBridge extends UserBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/videoConferences.ts b/apps/meteor/app/apps/server/bridges/videoConferences.ts index f022662566cb..b8362622b395 100644 --- a/apps/meteor/app/apps/server/bridges/videoConferences.ts +++ b/apps/meteor/app/apps/server/bridges/videoConferences.ts @@ -3,7 +3,7 @@ import type { AppVideoConference, VideoConference } from '@rocket.chat/apps-engi import type { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/videoConfProviders'; import { VideoConf } from '@rocket.chat/core-services'; -import type { AppServerOrchestrator } from '../orchestrator'; +import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { videoConfProviders } from '../../../../server/lib/videoConfProviders'; import type { AppVideoConferencesConverter } from '../converters/videoConferences'; diff --git a/apps/meteor/app/apps/server/communication/methods.ts b/apps/meteor/app/apps/server/communication/methods.ts deleted file mode 100644 index 3757314cf387..000000000000 --- a/apps/meteor/app/apps/server/communication/methods.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import type { SettingValue } from '@rocket.chat/core-typings'; -import { Settings } from '@rocket.chat/models'; - -import { hasPermission } from '../../../authorization/server'; -import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; -import type { AppServerOrchestrator } from '../orchestrator'; - -const waitToLoad = function (orch: AppServerOrchestrator): unknown { - return new Promise((resolve) => { - const id = setInterval(() => { - if (orch.isEnabled() && orch.isLoaded()) { - clearInterval(id); - resolve(); - } - }, 100); - }); -}; - -const waitToUnload = function (orch: AppServerOrchestrator): unknown { - return new Promise((resolve) => { - const id = setInterval(() => { - if (!orch.isEnabled() && !orch.isLoaded()) { - clearInterval(id); - resolve(); - } - }, 100); - }); -}; - -export class AppMethods { - private orch: AppServerOrchestrator; - - constructor(orch: AppServerOrchestrator) { - this.orch = orch; - - this.addMethods(); - } - - isEnabled(): SettingValue { - return typeof this.orch !== 'undefined' && this.orch.isEnabled(); - } - - isLoaded(): boolean { - return Boolean(typeof this.orch !== 'undefined' && this.orch.isEnabled() && this.orch.isLoaded()); - } - - private addMethods(): void { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const instance = this; - - Meteor.methods({ - 'apps/is-enabled'() { - return instance.isEnabled(); - }, - - 'apps/is-loaded'() { - return instance.isLoaded(); - }, - - 'apps/go-enable': twoFactorRequired(function _appsGoEnable() { - const uid = Meteor.userId(); - if (!uid) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'apps/go-enable', - }); - } - - if (!hasPermission(uid, 'manage-apps')) { - throw new Meteor.Error('error-action-not-allowed', 'Not allowed', { - method: 'apps/go-enable', - }); - } - - Settings.updateValueById('Apps_Framework_enabled', true); - - Promise.await(waitToLoad(instance.orch)); - }), - - 'apps/go-disable': twoFactorRequired(function _appsGoDisable() { - const uid = Meteor.userId(); - if (!uid) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'apps/go-enable', - }); - } - - if (!hasPermission(uid, 'manage-apps')) { - throw new Meteor.Error('error-action-not-allowed', 'Not allowed', { - method: 'apps/go-enable', - }); - } - - Settings.updateValueById('Apps_Framework_enabled', false); - - Promise.await(waitToUnload(instance.orch)); - }), - }); - } -} diff --git a/apps/meteor/app/apps/server/converters/departments.js b/apps/meteor/app/apps/server/converters/departments.js index 3d5a76a0e1e1..0954250eb021 100644 --- a/apps/meteor/app/apps/server/converters/departments.js +++ b/apps/meteor/app/apps/server/converters/departments.js @@ -1,5 +1,5 @@ import LivechatDepartment from '../../../models/server/models/LivechatDepartment'; -import { transformMappedData } from '../../lib/misc/transformMappedData'; +import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; export class AppDepartmentsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/index.js b/apps/meteor/app/apps/server/converters/index.js index d5fe67636dc0..c1f60ff18162 100644 --- a/apps/meteor/app/apps/server/converters/index.js +++ b/apps/meteor/app/apps/server/converters/index.js @@ -3,5 +3,17 @@ import { AppRoomsConverter } from './rooms'; import { AppSettingsConverter } from './settings'; import { AppUsersConverter } from './users'; import { AppVideoConferencesConverter } from './videoConferences'; +import { AppDepartmentsConverter } from './departments'; +import { AppUploadsConverter } from './uploads'; +import { AppVisitorsConverter } from './visitors'; -export { AppMessagesConverter, AppRoomsConverter, AppSettingsConverter, AppUsersConverter, AppVideoConferencesConverter }; +export { + AppMessagesConverter, + AppRoomsConverter, + AppSettingsConverter, + AppUsersConverter, + AppVideoConferencesConverter, + AppDepartmentsConverter, + AppUploadsConverter, + AppVisitorsConverter, +}; diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 25931452edd0..5eca5f06717b 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -1,7 +1,7 @@ import { Random } from 'meteor/random'; import { Messages, Rooms, Users } from '../../../models/server'; -import { transformMappedData } from '../../lib/misc/transformMappedData'; +import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; export class AppMessagesConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 4a9f6225af15..ee60db4fc29d 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -2,7 +2,7 @@ import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; import { LivechatVisitors } from '@rocket.chat/models'; import { Rooms, Users, LivechatDepartment } from '../../../models/server'; -import { transformMappedData } from '../../lib/misc/transformMappedData'; +import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; export class AppRoomsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/uploads.js b/apps/meteor/app/apps/server/converters/uploads.js index d386e52fdcac..0d8362c03ef2 100644 --- a/apps/meteor/app/apps/server/converters/uploads.js +++ b/apps/meteor/app/apps/server/converters/uploads.js @@ -1,6 +1,6 @@ import { Uploads } from '@rocket.chat/models'; -import { transformMappedData } from '../../lib/misc/transformMappedData'; +import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; export class AppUploadsConverter { constructor(orch) { diff --git a/apps/meteor/app/apps/server/converters/visitors.js b/apps/meteor/app/apps/server/converters/visitors.js index 361aa3758c6a..e54b7db257e5 100644 --- a/apps/meteor/app/apps/server/converters/visitors.js +++ b/apps/meteor/app/apps/server/converters/visitors.js @@ -1,6 +1,6 @@ import { LivechatVisitors } from '@rocket.chat/models'; -import { transformMappedData } from '../../lib/misc/transformMappedData'; +import { transformMappedData } from '../../../../ee/lib/misc/transformMappedData'; // TODO: check if functions from this converter can be async export class AppVisitorsConverter { diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index e0b080cd0688..5fd8b84025fa 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -17,7 +17,7 @@ import { isValidAttemptByUser, isValidLoginAttemptByIp } from '../lib/restrictLo import './settings'; import { getClientAddress } from '../../../../server/lib/getClientAddress'; import { getNewUserRoles } from '../../../../server/services/user/lib/getNewUserRoles'; -import { AppEvents, Apps } from '../../../apps/server/orchestrator'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser'; import { safeHtmlDots } from '../../../../lib/utils/safeHtmlDots'; diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.js b/apps/meteor/app/file-upload/server/lib/FileUpload.js index afcec09bc24f..5b165b11ae6d 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.js +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.js @@ -23,7 +23,7 @@ import { canAccessRoom } from '../../../authorization/server/functions/canAccess import { fileUploadIsValidContentType } from '../../../utils/lib/fileUploadRestrictions'; import { isValidJWT, generateJWT } from '../../../utils/server/lib/JWTHelper'; import { Messages } from '../../../models/server'; -import { AppEvents, Apps } from '../../../apps/server'; +import { AppEvents, Apps } from '../../../../ee/server/apps'; import { streamToBuffer } from './streamToBuffer'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 8429fea4e7d7..0ae7d3454f62 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor'; import type { IUser, IRoom } from '@rocket.chat/core-typings'; import { Team } from '@rocket.chat/core-services'; -import { AppEvents, Apps } from '../../../apps/server'; +import { AppEvents, Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { Messages, Rooms, Subscriptions, Users } from '../../../models/server'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/lib/server/functions/createDirectRoom.ts b/apps/meteor/app/lib/server/functions/createDirectRoom.ts index ac1ad5c75d50..2db347793465 100644 --- a/apps/meteor/app/lib/server/functions/createDirectRoom.ts +++ b/apps/meteor/app/lib/server/functions/createDirectRoom.ts @@ -7,7 +7,7 @@ import type { MatchKeysAndValues } from 'mongodb'; import type { ICreateRoomParams } from '@rocket.chat/core-services'; import { Users, Rooms } from '../../../models/server'; -import { Apps } from '../../../apps/server'; +import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { settings } from '../../../settings/server'; import { getDefaultSubscriptionPref } from '../../../utils/server'; diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 9d7348752864..0291eaf89556 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -5,7 +5,7 @@ import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typ import { Team } from '@rocket.chat/core-services'; import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services'; -import { Apps } from '../../../apps/server'; +import { Apps } from '../../../../ee/server/apps'; import { addUserRoles } from '../../../../server/lib/roles/addUserRoles'; import { callbacks } from '../../../../lib/callbacks'; import { Messages, Rooms, Subscriptions, Users } from '../../../models/server'; diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index 7ec2b736388d..87841dc6229b 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -7,7 +7,7 @@ import { FileUpload } from '../../../file-upload/server'; import { settings } from '../../../settings/server'; import { Messages, Rooms } from '../../../models/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Apps } from '../../../apps/server'; +import { Apps } from '../../../../ee/server/apps'; export const deleteMessage = async function (message: IMessage, user: IUser): Promise { const deletedMsg = Messages.findOneById(message._id); diff --git a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts index 4e1327ad3a2d..7bdf20e3f9a2 100644 --- a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts +++ b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts @@ -5,7 +5,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Team } from '@rocket.chat/core-services'; import { Rooms, Messages, Subscriptions } from '../../../models/server'; -import { AppEvents, Apps } from '../../../apps/server'; +import { AppEvents, Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; export const removeUserFromRoom = async function ( diff --git a/apps/meteor/app/lib/server/functions/saveUser.js b/apps/meteor/app/lib/server/functions/saveUser.js index 3d8f2548a8a3..5f6eeb411c37 100644 --- a/apps/meteor/app/lib/server/functions/saveUser.js +++ b/apps/meteor/app/lib/server/functions/saveUser.js @@ -14,7 +14,7 @@ import { saveUserIdentity } from './saveUserIdentity'; import { checkEmailAvailability, checkUsernameAvailability, setUserAvatar, setEmail, setStatusText } from '.'; import { Users } from '../../../models/server'; import { callbacks } from '../../../../lib/callbacks'; -import { AppEvents, Apps } from '../../../apps/server/orchestrator'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser'; import { trim } from '../../../../lib/utils/stringUtils'; diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.js index c65460ac4be1..d11a679f1fc2 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.js @@ -3,11 +3,10 @@ import { Match, check } from 'meteor/check'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; import { Messages } from '../../../models/server'; -import { Apps } from '../../../apps/server'; +import { Apps } from '../../../../ee/server/apps'; import { isURL } from '../../../../lib/utils/isURL'; import { FileUpload } from '../../../file-upload/server'; import { hasPermission } from '../../../authorization/server'; -import { SystemLogger } from '../../../../server/lib/logger/system'; import { parseUrlsInMessage } from './parseUrlsInMessage'; import { isRelativeURL } from '../../../../lib/utils/isRelativeURL'; import notifications from '../../../notifications/server/lib/Notifications'; @@ -220,10 +219,6 @@ export const sendMessage = function (user, message, room, upsert = false) { if (Apps && Apps.isLoaded()) { const prevent = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentPrevent', message)); if (prevent) { - if (settings.get('Apps_Framework_Development_Mode')) { - SystemLogger.info({ msg: 'A Rocket.Chat App prevented the message sending.', message }); - } - return; } diff --git a/apps/meteor/app/lib/server/functions/updateMessage.ts b/apps/meteor/app/lib/server/functions/updateMessage.ts index 9d18577b46c3..e633c299c838 100644 --- a/apps/meteor/app/lib/server/functions/updateMessage.ts +++ b/apps/meteor/app/lib/server/functions/updateMessage.ts @@ -4,7 +4,7 @@ import { Meteor } from 'meteor/meteor'; import { Messages, Rooms } from '../../../models/server'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { Apps } from '../../../apps/server'; +import { Apps } from '../../../../ee/server/apps'; import { parseUrlsInMessage } from './parseUrlsInMessage'; export const updateMessage = function (message: IMessage, user: IUser, originalMessage?: IMessage): void { diff --git a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts index 20707cc84433..d409a46dbabc 100644 --- a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts +++ b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts @@ -6,7 +6,7 @@ import { SHA256 } from 'meteor/sha'; import { settings } from '../../../settings/server'; import { Users } from '../../../models/server'; import { deleteUser } from '../functions'; -import { AppEvents, Apps } from '../../../apps/server/orchestrator'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { trim } from '../../../../lib/utils/stringUtils'; Meteor.methods({ diff --git a/apps/meteor/app/livechat/server/lib/Helper.js b/apps/meteor/app/livechat/server/lib/Helper.js index 3757b324a819..fe82cb23f0db 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.js +++ b/apps/meteor/app/livechat/server/lib/Helper.js @@ -22,7 +22,7 @@ import { RoutingManager } from './RoutingManager'; import { callbacks } from '../../../../lib/callbacks'; import { Logger } from '../../../logger'; import { settings } from '../../../settings/server'; -import { Apps, AppEvents } from '../../../apps/server'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { sendNotification } from '../../../lib/server'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { queueInquiry, saveQueueInquiry } from './QueueManager'; diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 23c86a75bb47..fb829c14aaf9 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -47,7 +47,7 @@ import { updateMessage } from '../../../lib/server/functions/updateMessage'; import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; import { FileUpload } from '../../../file-upload/server'; import { normalizeTransferredByData, parseAgentCustomFields, updateDepartmentAgents, validateEmail } from './Helper'; -import { Apps, AppEvents } from '../../../apps/server'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { businessHourManager } from '../business-hour'; import { addUserRoles } from '../../../../server/lib/roles/addUserRoles'; import { removeUserFromRoles } from '../../../../server/lib/roles/removeUserFromRoles'; diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 10d6f5822596..6931ab63ae0c 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -13,7 +13,7 @@ import { callbacks } from '../../../../lib/callbacks'; import { Logger } from '../../../logger/server'; import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor } from './LivechatTyped.d'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; -import { Apps, AppEvents } from '../../../apps/server'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; import { Messages as LegacyMessage } from '../../../models/server'; class LivechatClass { diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.js b/apps/meteor/app/livechat/server/lib/RoutingManager.js index a0cf60c71c56..be5b01152e85 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.js +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.js @@ -14,7 +14,7 @@ import { import { callbacks } from '../../../../lib/callbacks'; import { Logger } from '../../../../server/lib/logger/Logger'; import { LivechatRooms, Rooms, Messages, Users, LivechatInquiry, Subscriptions } from '../../../models/server'; -import { Apps, AppEvents } from '../../../apps/server'; +import { Apps, AppEvents } from '../../../../ee/server/apps'; const logger = new Logger('RoutingManager'); diff --git a/apps/meteor/app/mailer/server/api.ts b/apps/meteor/app/mailer/server/api.ts index 18910fa20c4e..451d67a3f2fa 100644 --- a/apps/meteor/app/mailer/server/api.ts +++ b/apps/meteor/app/mailer/server/api.ts @@ -10,7 +10,7 @@ import { Settings } from '@rocket.chat/models'; import { settings } from '../../settings/server'; import { replaceVariables } from './replaceVariables'; -import { Apps } from '../../apps/server'; +import { Apps } from '../../../ee/server/apps'; import { validateEmail } from '../../../lib/emailValidator'; import { strLeft, strRightBack } from '../../../lib/utils/stringUtils'; diff --git a/apps/meteor/app/message-pin/server/pinMessage.js b/apps/meteor/app/message-pin/server/pinMessage.js index c176d16acd45..48fbd43fa435 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.js +++ b/apps/meteor/app/message-pin/server/pinMessage.js @@ -7,7 +7,7 @@ import { isTheLastMessage } from '../../lib/server'; import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL'; import { canAccessRoom, hasPermission, roomAccessAttributes } from '../../authorization/server'; import { Subscriptions, Messages, Users, Rooms } from '../../models/server'; -import { Apps, AppEvents } from '../../apps/server/orchestrator'; +import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; const recursiveRemove = (msg, deep = 1) => { if (!msg) { diff --git a/apps/meteor/app/message-star/server/starMessage.js b/apps/meteor/app/message-star/server/starMessage.js index 8e4292170750..98a7fb47cea0 100644 --- a/apps/meteor/app/message-star/server/starMessage.js +++ b/apps/meteor/app/message-star/server/starMessage.js @@ -4,7 +4,7 @@ import { settings } from '../../settings/server'; import { isTheLastMessage } from '../../lib/server'; import { canAccessRoom, roomAccessAttributes } from '../../authorization/server'; import { Subscriptions, Rooms, Messages } from '../../models/server'; -import { Apps, AppEvents } from '../../apps/server/orchestrator'; +import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; Meteor.methods({ starMessage(message) { diff --git a/apps/meteor/app/models/server/index.ts b/apps/meteor/app/models/server/index.ts index c09728335642..c01469c7ef70 100644 --- a/apps/meteor/app/models/server/index.ts +++ b/apps/meteor/app/models/server/index.ts @@ -10,13 +10,14 @@ import LivechatDepartmentAgents from './models/LivechatDepartmentAgents'; import LivechatRooms from './models/LivechatRooms'; import LivechatInquiry from './models/LivechatInquiry'; import ImportData from './models/ImportData'; +import AppsModel from './models/apps-model'; export { AppsLogsModel } from './models/apps-logs-model'; export { AppsPersistenceModel } from './models/apps-persistence-model'; -export { AppsModel } from './models/apps-model'; export { FederationRoomEvents } from './models/FederationRoomEvents'; export { + AppsModel, Base, BaseDb, Messages, diff --git a/apps/meteor/app/models/server/models/apps-model.js b/apps/meteor/app/models/server/models/apps-model.js index 086db3ccb7e6..50c870e9a20f 100644 --- a/apps/meteor/app/models/server/models/apps-model.js +++ b/apps/meteor/app/models/server/models/apps-model.js @@ -5,3 +5,5 @@ export class AppsModel extends Base { super('apps'); } } + +export default new AppsModel(); diff --git a/apps/meteor/app/reactions/server/setReaction.js b/apps/meteor/app/reactions/server/setReaction.js index bc58c1ffc226..72a6ed5fab7d 100644 --- a/apps/meteor/app/reactions/server/setReaction.js +++ b/apps/meteor/app/reactions/server/setReaction.js @@ -9,7 +9,7 @@ import { callbacks } from '../../../lib/callbacks'; import { emoji } from '../../emoji/server'; import { isTheLastMessage, msgStream } from '../../lib/server'; import { canAccessRoom, hasPermission } from '../../authorization/server'; -import { AppEvents, Apps } from '../../apps/server/orchestrator'; +import { AppEvents, Apps } from '../../../ee/server/apps/orchestrator'; const removeUserReaction = (message, reaction, username) => { message.reactions[reaction].usernames.splice(message.reactions[reaction].usernames.indexOf(username), 1); diff --git a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js index b326c4958951..e44af22aa166 100644 --- a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js +++ b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js @@ -1,12 +1,11 @@ import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; -import { Apps } from '../../../apps/server'; +import { Apps } from '../../../../ee/server/apps'; import { Info } from '../../../utils/server'; export function getAppsStatistics() { return { engineVersion: Info.marketplaceApiVersion, - enabled: Apps.isEnabled(), totalInstalled: Apps.isInitialized() && Apps.getManager().get().length, totalActive: Apps.isInitialized() && Apps.getManager().get({ enabled: true }).length, totalFailed: diff --git a/apps/meteor/app/threads/server/methods/followMessage.js b/apps/meteor/app/threads/server/methods/followMessage.js index 99df51f65462..b3a28dc89956 100644 --- a/apps/meteor/app/threads/server/methods/followMessage.js +++ b/apps/meteor/app/threads/server/methods/followMessage.js @@ -6,7 +6,7 @@ import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; import { canAccessRoomId } from '../../../authorization/server'; import { follow } from '../functions'; -import { Apps, AppEvents } from '../../../apps/server/orchestrator'; +import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; Meteor.methods({ followMessage({ mid }) { diff --git a/apps/meteor/app/threads/server/methods/unfollowMessage.js b/apps/meteor/app/threads/server/methods/unfollowMessage.js index 5381fd925d38..0ba2f38b7a5b 100644 --- a/apps/meteor/app/threads/server/methods/unfollowMessage.js +++ b/apps/meteor/app/threads/server/methods/unfollowMessage.js @@ -6,7 +6,7 @@ import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; import { canAccessRoomId } from '../../../authorization/server'; import { unfollow } from '../functions'; -import { Apps, AppEvents } from '../../../apps/server/orchestrator'; +import { Apps, AppEvents } from '../../../../ee/server/apps/orchestrator'; Meteor.methods({ unfollowMessage({ mid }) { diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts index 6cafa3c4f875..28c74d5efce9 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts @@ -1,6 +1,6 @@ import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { Utilities } from '../../../apps/lib/misc/Utilities'; +import { Utilities } from '../../../../ee/lib/misc/Utilities'; import { MessageAction } from '../../../ui-utils/client'; import { messageArgs } from '../../../../client/lib/utils/messageArgs'; import { t } from '../../../utils/client'; diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts index 2ad405bcf9c5..62273af61459 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts @@ -7,7 +7,7 @@ import { messageBox } from '../../../ui-utils/client'; import { applyButtonFilters } from './lib/applyButtonFilters'; import { triggerActionButtonAction } from '../ActionManager'; import { t } from '../../../utils/client'; -import { Utilities } from '../../../apps/lib/misc/Utilities'; +import { Utilities } from '../../../../ee/lib/misc/Utilities'; const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; diff --git a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts index 55d2e17ba0d3..7056a3cc299d 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts @@ -1,7 +1,7 @@ import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; import { addAction, deleteAction } from '../../../../client/views/room/lib/Toolbox'; -import { Utilities } from '../../../apps/lib/misc/Utilities'; +import { Utilities } from '../../../../ee/lib/misc/Utilities'; import { t } from '../../../utils/client'; import { triggerActionButtonAction } from '../ActionManager'; import { applyButtonFilters } from './lib/applyButtonFilters'; diff --git a/apps/meteor/client/components/AdministrationList/AdministrationList.spec.tsx b/apps/meteor/client/components/AdministrationList/AdministrationList.spec.tsx index ce4601721ba1..c3f905f97e09 100644 --- a/apps/meteor/client/components/AdministrationList/AdministrationList.spec.tsx +++ b/apps/meteor/client/components/AdministrationList/AdministrationList.spec.tsx @@ -23,62 +23,39 @@ describe('AdministrationList', () => { }; it('should render all model list', async () => { - const AdministrationList = loadMock(); + const AdministrationList = loadMock({ + '../../../ee/client/hooks/useHasLicenseModule': { + useHasLicenseModule: () => true, + }, + '@rocket.chat/ui-contexts': { + usePermission: (key: string) => ['can-audit-log', 'manage-apps', 'can-audit'].includes(key), + useAtLeastOnePermission: (keys: string[]) => keys.some((key) => ['manage-emoji'].includes(key)), + }, + }); - render( - null} - />, - ); + render( null} />); expect(screen.getByText('Administration Model List')).to.exist; expect(screen.getByText('Apps Model List')).to.exist; expect(screen.getByText('Audit Model List')).to.exist; }); - it('should render nothing when no permission', async () => { - const AdministrationList = loadMock(); - - render( - null} - />, - ); - - expect(screen.queryByText('Administration Model List')).to.not.exist; - expect(screen.queryByText('Apps Model List')).to.not.exist; - expect(screen.queryByText('Audit Model List')).to.not.exist; - }); - it('should render administration model list when has account box item', async () => { - const AdministrationList = loadMock(); + const AdministrationList = loadMock({ + '../../../ee/client/hooks/useHasLicenseModule': { + 'useHasLicenseModule': () => false, + + '@rocket.chat/ui-contexts': { + usePermission: () => false, + useAtLeastOnePermission: () => false, + }, + }, + }); - render( - null} - />, - ); + render( null} />); expect(screen.getByText('Administration Model List')).to.exist; - expect(screen.queryByText('Apps Model List')).to.not.exist; + expect(screen.queryByText('Apps Model List')).to.exist; expect(screen.queryByText('Audit Model List')).to.not.exist; }); @@ -88,19 +65,17 @@ describe('AdministrationList', () => { AccountBoxItem: {}, isAppAccountBoxItem: () => true, }, + '../../../ee/client/hooks/useHasLicenseModule': { + 'useHasLicenseModule': () => false, + + '@rocket.chat/ui-contexts': { + usePermission: () => false, + useAtLeastOnePermission: () => false, + }, + }, }); - render( - null} - />, - ); + render( null} />); expect(screen.getByText('Apps Model List')).to.exist; expect(screen.queryByText('Administration Model List')).to.not.exist; diff --git a/apps/meteor/client/components/AdministrationList/AdministrationList.tsx b/apps/meteor/client/components/AdministrationList/AdministrationList.tsx index e59d6e159f7b..8270738b0e11 100644 --- a/apps/meteor/client/components/AdministrationList/AdministrationList.tsx +++ b/apps/meteor/client/components/AdministrationList/AdministrationList.tsx @@ -1,9 +1,11 @@ import { OptionDivider } from '@rocket.chat/fuselage'; -import type { FC } from 'react'; +import { useAtLeastOnePermission, usePermission } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; import React, { Fragment } from 'react'; import type { AccountBoxItem, IAppAccountBoxItem } from '../../../app/ui-utils/client/lib/AccountBox'; import { isAppAccountBoxItem } from '../../../app/ui-utils/client/lib/AccountBox'; +import { useHasLicenseModule } from '../../../ee/client/hooks/useHasLicenseModule'; import AdministrationModelList from './AdministrationModelList'; import AppsModelList from './AppsModelList'; import AuditModelList from './AuditModelList'; @@ -11,31 +13,42 @@ import AuditModelList from './AuditModelList'; type AdministrationListProps = { accountBoxItems: (IAppAccountBoxItem | AccountBoxItem)[]; onDismiss: () => void; - hasAdminPermission: boolean; - hasAuditLicense: boolean; - hasAuditPermission: boolean; - hasAuditLogPermission: boolean; - hasManageApps: boolean; }; -const AdministrationList: FC = ({ - accountBoxItems, - hasAuditPermission, - hasAuditLogPermission, - hasManageApps, - hasAdminPermission, - onDismiss, -}) => { +const ADMIN_PERMISSIONS = [ + 'view-logs', + 'manage-emoji', + 'manage-sounds', + 'view-statistics', + 'manage-oauth-apps', + 'view-privileged-setting', + 'manage-selected-settings', + 'view-room-administration', + 'view-user-administration', + 'access-setting-permissions', + 'manage-outgoing-integrations', + 'manage-incoming-integrations', + 'manage-own-outgoing-integrations', + 'manage-own-incoming-integrations', + 'view-engagement-dashboard', +]; + +const AdministrationList = ({ accountBoxItems, onDismiss }: AdministrationListProps): ReactElement => { + const hasAuditLicense = useHasLicenseModule('auditing') === true; + const hasAdminPermission = useAtLeastOnePermission(ADMIN_PERMISSIONS); + const hasManageAppsPermission = usePermission('manage-apps'); + const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; + const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; + const appBoxItems = accountBoxItems.filter((item): item is IAppAccountBoxItem => isAppAccountBoxItem(item)); const adminBoxItems = accountBoxItems.filter((item): item is AccountBoxItem => !isAppAccountBoxItem(item)); const showAudit = hasAuditPermission || hasAuditLogPermission; - const showManageApps = hasManageApps || !!appBoxItems.length; const showAdmin = hasAdminPermission || !!adminBoxItems.length; const showWorkspace = hasAdminPermission; const list = [ showAdmin && , - showManageApps && , + , showAudit && , ]; diff --git a/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx b/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx index 5a65554cdbe1..bfd750435ed0 100644 --- a/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx +++ b/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx @@ -14,21 +14,35 @@ describe('AppsModelList', () => { '../../../app/ui-message/client/ActionManager': { triggerActionButtonAction: {}, }, + '../../views/marketplace/hooks/useAppRequestStats': { + useAppRequestStats: () => { + return { + isLoading: false, + isSuccess: true, + data: { + data: { + totalUnseen: 5, + }, + }, + }; + }, + }, ...stubs, }).default; }; - it('should render apps', async () => { + it('should render all apps options when a user has manage apps permission', async () => { const AppsModelList = loadMock(); - render( null} appBoxItems={[]} />); + render( null} appBoxItems={[]} appsManagementAllowed />); expect(screen.getByText('Apps')).to.exist; expect(screen.getByText('Marketplace')).to.exist; expect(screen.getByText('Installed')).to.exist; + expect(screen.getByText('Requested')).to.exist; }); - it('should not render marketplace and installed when does not have permission', async () => { + it('should render only marketplace and installed options when a user does not have manage apps permission', async () => { const AppsModelList = loadMock({ '@rocket.chat/ui-contexts': { 'useAtLeastOnePermission': (): boolean => false, @@ -36,11 +50,12 @@ describe('AppsModelList', () => { }, }); - render( null} appBoxItems={[]} />); + render( null} appBoxItems={[]} appsManagementAllowed={false} />); expect(screen.getByText('Apps')).to.exist; - expect(screen.queryByText('Marketplace')).to.not.exist; - expect(screen.queryByText('Installed')).to.not.exist; + expect(screen.getByText('Marketplace')).to.exist; + expect(screen.getByText('Installed')).to.exist; + expect(screen.queryByText('Requested')).to.not.exist; }); context('when clicked', () => { @@ -51,27 +66,38 @@ describe('AppsModelList', () => { return {children}; }; - it('should go to admin marketplace', async () => { + it('should go to marketplace', async () => { const AppsModelList = loadMock(); - render(, { wrapper: ProvidersMock }); + render(, { wrapper: ProvidersMock }); const button = screen.getByText('Marketplace'); - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('admin-marketplace')); + await waitFor(() => expect(pushRoute).to.have.been.called.with('marketplace', { context: 'explore', page: 'list' })); await waitFor(() => expect(handleDismiss).to.have.been.called()); }); it('should go to installed', async () => { const AppsModelList = loadMock(); - render(, { wrapper: ProvidersMock }); + render(, { wrapper: ProvidersMock }); const button = screen.getByText('Installed'); userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('admin-marketplace', { context: 'installed', page: 'list' })); + await waitFor(() => expect(pushRoute).to.have.been.called.with('marketplace', { context: 'installed', page: 'list' })); + await waitFor(() => expect(handleDismiss).to.have.been.called()); + }); + + it('should go to requested if user has manage apps permission', async () => { + const AppsModelList = loadMock(); + + render(, { wrapper: ProvidersMock }); + + const button = screen.getByText('Requested'); + + userEvent.click(button); + await waitFor(() => expect(pushRoute).to.have.been.called.with('marketplace', { context: 'requested', page: 'list' })); await waitFor(() => expect(handleDismiss).to.have.been.called()); }); @@ -85,7 +111,7 @@ describe('AppsModelList', () => { }, }); - render(, { + render(, { wrapper: ProvidersMock, }); diff --git a/apps/meteor/client/components/AdministrationList/AppsModelList.tsx b/apps/meteor/client/components/AdministrationList/AppsModelList.tsx index c5dea2dfe18b..29c209eabac7 100644 --- a/apps/meteor/client/components/AdministrationList/AppsModelList.tsx +++ b/apps/meteor/client/components/AdministrationList/AppsModelList.tsx @@ -1,47 +1,68 @@ -import { OptionTitle } from '@rocket.chat/fuselage'; +import { Badge, OptionSkeleton, OptionTitle } from '@rocket.chat/fuselage'; import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; import { triggerActionButtonAction } from '../../../app/ui-message/client/ActionManager'; import type { IAppAccountBoxItem } from '../../../app/ui-utils/client/lib/AccountBox'; +import { useAppRequestStats } from '../../views/marketplace/hooks/useAppRequestStats'; import ListItem from '../Sidebar/ListItem'; type AppsModelListProps = { appBoxItems: IAppAccountBoxItem[]; - appsManagementAllowed: boolean; + appsManagementAllowed?: boolean; onDismiss: () => void; }; const AppsModelList = ({ appBoxItems, appsManagementAllowed, onDismiss }: AppsModelListProps): ReactElement => { const t = useTranslation(); - const marketplaceRoute = useRoute('admin-marketplace'); + const marketplaceRoute = useRoute('marketplace'); const page = 'list'; + const appRequestStats = useAppRequestStats(); + return ( <> {t('Apps')}
    - {appsManagementAllowed && ( - <> - { - marketplaceRoute.push({ context: 'all', page }); - onDismiss(); - }} - /> - { - marketplaceRoute.push({ context: 'installed', page }); - onDismiss(); - }} - /> - - )} + <> + { + marketplaceRoute.push({ context: 'explore', page }); + onDismiss(); + }} + /> + { + marketplaceRoute.push({ context: 'installed', page }); + onDismiss(); + }} + /> + + {appsManagementAllowed && ( + <> + {appRequestStats.isLoading && } + {appRequestStats.isSuccess && ( + { + marketplaceRoute.push({ context: 'requested', page }); + onDismiss(); + }} + > + {appRequestStats.isSuccess && appRequestStats.data.data.totalUnseen > 0 && ( + {appRequestStats.data.data.totalUnseen} + )} + + )} + + )} + {appBoxItems.length > 0 && ( <> {appBoxItems.map((item, key) => { diff --git a/apps/meteor/client/components/Sidebar/Header.tsx b/apps/meteor/client/components/Sidebar/Header.tsx index f46d761ae501..2ae61f18bcdc 100644 --- a/apps/meteor/client/components/Sidebar/Header.tsx +++ b/apps/meteor/client/components/Sidebar/Header.tsx @@ -16,7 +16,7 @@ const Header: FC = ({ title, onClose, children, ...props }) => { {(title || onClose) && ( {title && ( - + {title} )} diff --git a/apps/meteor/client/components/Sidebar/ListItem.tsx b/apps/meteor/client/components/Sidebar/ListItem.tsx index 44ca4098353f..540308334fea 100644 --- a/apps/meteor/client/components/Sidebar/ListItem.tsx +++ b/apps/meteor/client/components/Sidebar/ListItem.tsx @@ -6,14 +6,17 @@ type ListItemProps = { text: ReactNode; icon?: ComponentProps['name']; input?: any; + loading?: boolean; action?: MouseEventHandler; + children?: ReactNode; }; -const ListItem = ({ text, icon, input, action }: ListItemProps): ReactElement => ( +const ListItem = ({ text, icon, input, action, children }: ListItemProps): ReactElement => ( ); diff --git a/apps/meteor/client/components/Sidebar/Sidebar.tsx b/apps/meteor/client/components/Sidebar/Sidebar.tsx index b23e0386245e..103c680770c4 100644 --- a/apps/meteor/client/components/Sidebar/Sidebar.tsx +++ b/apps/meteor/client/components/Sidebar/Sidebar.tsx @@ -1,11 +1,9 @@ -import { Box } from '@rocket.chat/fuselage'; +import { Sidebar as FuselageSidebar } from '@rocket.chat/fuselage'; import type { FC } from 'react'; import React from 'react'; const Sidebar: FC = ({ children, ...props }) => ( - - {children} - + ); export default Sidebar; diff --git a/apps/meteor/client/components/Sidebar/SidebarGenericItem.tsx b/apps/meteor/client/components/Sidebar/SidebarGenericItem.tsx index 7fc522c31f09..b43509921c3b 100644 --- a/apps/meteor/client/components/Sidebar/SidebarGenericItem.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarGenericItem.tsx @@ -13,12 +13,12 @@ type SidebarGenericItemProps = { hover: typeof colors[string]; active: typeof colors[string]; }; - textColor?: string; + externalUrl?: boolean; }; -const SidebarGenericItem = ({ href, active, children, ...props }: SidebarGenericItemProps): ReactElement => ( - - +const SidebarGenericItem = ({ href, active, externalUrl, children, ...props }: SidebarGenericItemProps): ReactElement => ( + + {children} diff --git a/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx b/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx index 123f55997629..6962791959ff 100644 --- a/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx @@ -1,8 +1,10 @@ +import { Divider } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; -import React, { memo } from 'react'; +import React, { Fragment, memo } from 'react'; import type { SidebarItem } from '../../lib/createSidebarItems'; +import { isSidebarItem } from '../../lib/createSidebarItems'; import SidebarNavigationItem from './SidebarNavigationItem'; type SidebarItemsAssemblerProps = { @@ -12,19 +14,26 @@ type SidebarItemsAssemblerProps = { const SidebarItemsAssembler: FC = ({ items, currentPath }) => { const t = useTranslation(); + return ( <> - {items.map(({ href, pathSection, i18nLabel, name, icon, permissionGranted, pathGroup, tag }) => ( - [0])} - key={i18nLabel || name} - currentPath={currentPath} - tag={t.has(tag as Parameters[0]) ? t(tag as Parameters[0]) : undefined} - /> + {items.map((props) => ( + + {isSidebarItem(props) ? ( + [0])} + currentPath={currentPath} + tag={t.has(props.tag) ? t(props.tag) : props.tag} + externalUrl={props.externalUrl} + /> + ) : ( + + )} + ))} ); diff --git a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx index 131be118e30d..a45cfec12f70 100644 --- a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx @@ -1,7 +1,7 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Box, Icon, Tag } from '@rocket.chat/fuselage'; +import type { IconProps } from '@rocket.chat/fuselage'; import { useRoutePath } from '@rocket.chat/ui-contexts'; -import type { FC } from 'react'; +import type { FC, ReactElement } from 'react'; import React, { memo, useMemo } from 'react'; import SidebarGenericItem from './SidebarGenericItem'; @@ -14,6 +14,8 @@ type SidebarNavigationItemProps = { label?: string; tag?: string; currentPath?: string; + externalUrl?: boolean; + badge?: () => ReactElement; }; const SidebarNavigationItem: FC = ({ @@ -24,20 +26,34 @@ const SidebarNavigationItem: FC = ({ label, currentPath, tag, + externalUrl, + // eslint-disable-next-line @typescript-eslint/naming-convention + badge: Badge, }) => { const params = useMemo(() => ({ group: pathGroup }), [pathGroup]); const path = useRoutePath(pathSection, params); - const isActive = currentPath?.includes(path as string); + const isActive = !!path && currentPath?.includes(path as string); if (permissionGranted === false || (typeof permissionGranted === 'function' && !permissionGranted())) { return null; } + return ( - + {icon && } - + {label} {tag && {tag}} + {Badge ? : null} ); }; diff --git a/apps/meteor/client/components/UpsellModal.stories.tsx b/apps/meteor/client/components/UpsellModal.stories.tsx new file mode 100644 index 000000000000..177efdab2876 --- /dev/null +++ b/apps/meteor/client/components/UpsellModal.stories.tsx @@ -0,0 +1,24 @@ +import { action } from '@storybook/addon-actions'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import React from 'react'; + +import UpsellModal from './UpsellModal'; + +export default { + title: 'Components/UpsellModal', + component: UpsellModal, +} as ComponentMeta; + +export const Example: ComponentStory = () => ( + +); diff --git a/apps/meteor/client/components/UpsellModal.tsx b/apps/meteor/client/components/UpsellModal.tsx new file mode 100644 index 000000000000..08cb1171e9ee --- /dev/null +++ b/apps/meteor/client/components/UpsellModal.tsx @@ -0,0 +1,75 @@ +import type { Icon } from '@rocket.chat/fuselage'; +import { Box, Button, Modal } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactNode, ReactElement, ComponentProps } from 'react'; +import React from 'react'; + +type UpsellModalProps = { + children?: ReactNode; + tagline?: ReactNode; + cancelText?: ReactNode; + confirmText?: ReactNode; + title: string | ReactElement; + subtitle?: string | ReactElement; + description?: string | ReactElement; + icon?: ComponentProps['name']; + img: ComponentProps['src']; + onCancel?: () => void; + onClose?: () => void; + onConfirm: () => void; +}; + +const UpsellModal = ({ + tagline, + title, + subtitle, + img, + cancelText, + confirmText, + icon, + description, + onCancel, + onConfirm, + onClose = onCancel, +}: UpsellModalProps) => { + const t = useTranslation(); + + return ( + + + {icon && } + + {tagline ?? t('Enterprise_capability')} + {title} + + + + + + {subtitle && ( + + {subtitle} + + )} +
    + {description && {description}} +
    + + + {onCancel && ( + + )} + {onConfirm && ( + + )} + + +
    + ); +}; + +export default UpsellModal; diff --git a/apps/meteor/client/importPackages.ts b/apps/meteor/client/importPackages.ts index 56cca0ed9916..be88769a506d 100644 --- a/apps/meteor/client/importPackages.ts +++ b/apps/meteor/client/importPackages.ts @@ -31,7 +31,6 @@ import '../app/message-mark-as-unread/client'; import '../app/nextcloud/client'; import '../app/otr/client'; import '../app/push-notifications/client'; -import '../app/apps/client'; import '../app/slackbridge/client'; import '../app/slashcommands-archiveroom/client'; import '../app/slashcommand-asciiarts/client'; diff --git a/apps/meteor/client/lib/createSidebarItems.ts b/apps/meteor/client/lib/createSidebarItems.ts index ff7eb08d0430..89d8dac42a90 100644 --- a/apps/meteor/client/lib/createSidebarItems.ts +++ b/apps/meteor/client/lib/createSidebarItems.ts @@ -1,6 +1,7 @@ import type { IconProps } from '@rocket.chat/fuselage'; +import type { ReactElement } from 'react'; -export type SidebarItem = { +type Item = { i18nLabel: string; href?: string; icon?: IconProps['name']; @@ -9,7 +10,11 @@ export type SidebarItem = { pathSection?: string; pathGroup?: string; name?: string; + externalUrl?: boolean; + badge?: () => ReactElement; }; +export type SidebarItem = Item | { divider: boolean; i18nLabel: string }; // TODO: Remove this when we have a better way to handle dividers +export const isSidebarItem = (item: SidebarItem): item is Item => !('divider' in item); export const createSidebarItems = ( initialItems: SidebarItem[] = [], diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index 08cb64cadffe..a8087a60031a 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -12,6 +12,7 @@ import './templateHelpers'; import './methods'; import './startup'; import './views/admin'; +import './views/marketplace'; import './views/account'; import './views/teams'; import './templates'; diff --git a/apps/meteor/client/sidebar/header/actions/Administration.tsx b/apps/meteor/client/sidebar/header/actions/Administration.tsx index 3516743dd5d8..474b88fd50b2 100644 --- a/apps/meteor/client/sidebar/header/actions/Administration.tsx +++ b/apps/meteor/client/sidebar/header/actions/Administration.tsx @@ -1,39 +1,14 @@ import { Sidebar, Dropdown } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useAtLeastOnePermission } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes, VFC } from 'react'; import React, { useRef } from 'react'; import { createPortal } from 'react-dom'; import { AccountBox } from '../../../../app/ui-utils/client'; -import { useHasLicenseModule } from '../../../../ee/client/hooks/useHasLicenseModule'; import AdministrationList from '../../../components/AdministrationList/AdministrationList'; import { useReactiveValue } from '../../../hooks/useReactiveValue'; import { useDropdownVisibility } from '../hooks/useDropdownVisibility'; -const ADMIN_PERMISSIONS = [ - 'view-logs', - 'manage-emoji', - 'manage-sounds', - 'view-statistics', - 'manage-oauth-apps', - 'view-privileged-setting', - 'manage-selected-settings', - 'view-room-administration', - 'view-user-administration', - 'access-setting-permissions', - 'manage-outgoing-integrations', - 'manage-incoming-integrations', - 'manage-own-outgoing-integrations', - 'manage-own-incoming-integrations', - 'view-engagement-dashboard', -]; -const AUDIT_PERMISSIONS = ['can-audit']; -const AUDIT_LOG_PERMISSIONS = ['can-audit-log']; -const MANAGE_APPS_PERMISSIONS = ['manage-apps']; - -const AUDIT_LICENSE_MODULE = 'auditing'; - const Administration: VFC, 'is'>> = (props) => { const reference = useRef(null); const target = useRef(null); @@ -43,28 +18,13 @@ const Administration: VFC, 'is'>> = (props) => const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); const accountBoxItems = useReactiveValue(getAccountBoxItems); - const hasAuditLicense = useHasLicenseModule(AUDIT_LICENSE_MODULE) === true; - const hasAuditPermission = useAtLeastOnePermission(AUDIT_PERMISSIONS) && hasAuditLicense; - const hasAuditLogPermission = useAtLeastOnePermission(AUDIT_LOG_PERMISSIONS) && hasAuditLicense; - const hasManageApps = useAtLeastOnePermission(MANAGE_APPS_PERMISSIONS); - const hasAdminPermission = useAtLeastOnePermission(ADMIN_PERMISSIONS); - const showMenu = hasAuditPermission || hasAuditLogPermission || hasManageApps || hasAdminPermission || !!accountBoxItems.length; - return ( <> - {showMenu && toggle()} {...props} ref={reference} />} + toggle()} {...props} ref={reference} /> {isVisible && createPortal( - toggle(false)} - hasAdminPermission={hasAdminPermission} - hasAuditLicense={hasAuditLicense} - hasAuditPermission={hasAuditPermission} - hasAuditLogPermission={hasAuditLogPermission} - hasManageApps={hasManageApps} - /> + toggle(false)} /> , document.body, )} diff --git a/apps/meteor/client/views/admin/apps/AppsPage/AppsPage.tsx b/apps/meteor/client/views/admin/apps/AppsPage/AppsPage.tsx deleted file mode 100644 index 6de3e35bca96..000000000000 --- a/apps/meteor/client/views/admin/apps/AppsPage/AppsPage.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Button, ButtonGroup, Icon, Tabs } from '@rocket.chat/fuselage'; -import { useRoute, useSetting, useTranslation, useCurrentRoute, useRouteParameter } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React from 'react'; - -import Page from '../../../../components/Page'; -import AppsPageContent from './AppsPageContent'; - -const AppsPage = (): ReactElement => { - const t = useTranslation(); - - const isDevelopmentMode = useSetting('Apps_Framework_Development_Mode'); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const router = useRoute(currentRouteName); - - const context = useRouteParameter('context'); - - const handleUploadButtonClick = (): void => { - context && router.push({ context, page: 'install' }); - }; - - const handleMarketplaceTabClick = (): void => router.push({ context: 'all', page: 'list' }); - - const handleInstalledTabClick = (): void => router.push({ context: 'installed', page: 'list' }); - - return ( - - - - {Boolean(isDevelopmentMode) && ( - - )} - - - - - {t('Marketplace')} - - - {t('Installed')} - - - - - - - ); -}; - -export default AppsPage; diff --git a/apps/meteor/client/views/admin/apps/AppsWhatIsIt.tsx b/apps/meteor/client/views/admin/apps/AppsWhatIsIt.tsx deleted file mode 100644 index a4aff400ab0c..000000000000 --- a/apps/meteor/client/views/admin/apps/AppsWhatIsIt.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Button, Box, Throbber } from '@rocket.chat/fuselage'; -import { ExternalLink } from '@rocket.chat/ui-client'; -import { useRoute, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; -import type { FC } from 'react'; -import React, { useState } from 'react'; - -import { Apps } from '../../../../app/apps/client'; -import Page from '../../../components/Page'; - -const readMeUrl = 'https://go.rocket.chat/i/developing-an-app'; - -const AppsWhatIsIt: FC = () => { - const t = useTranslation(); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(false); - - const appsRouter = useRoute('admin-marketplace'); - const enableAppsEngine = useMethod('apps/go-enable'); - const isAppsEngineEnabled = useMethod('apps/is-enabled'); - - const handleClick = async (): Promise => { - setLoading(true); - try { - await enableAppsEngine(); - if (await isAppsEngineEnabled()) { - await Apps.getAppClientManager().initialize(); - await Apps.load(true); - } - appsRouter.push(); - } catch (e) { - setError(e instanceof Error ? e : new Error(String(e))); - } - }; - - return ( - - - - {error ? ( - - {error.message} - - ) : ( - -

    {t('Apps_WhatIsIt_paragraph1')}

    -

    {t('Apps_WhatIsIt_paragraph2')}

    -

    - {t('Apps_WhatIsIt_paragraph3')} -

    -

    {t('Apps_WhatIsIt_paragraph4')}

    - -
    - )} -
    -
    - ); -}; - -export default AppsWhatIsIt; diff --git a/apps/meteor/client/views/admin/cloud/CloudPage.tsx b/apps/meteor/client/views/admin/cloud/CloudPage.tsx index 9ffdf98e4aae..6a85815f5988 100644 --- a/apps/meteor/client/views/admin/cloud/CloudPage.tsx +++ b/apps/meteor/client/views/admin/cloud/CloudPage.tsx @@ -22,8 +22,9 @@ import WorkspaceLoginSection from './WorkspaceLoginSection'; import WorkspaceRegistrationSection from './WorkspaceRegistrationSection'; import { cloudConsoleUrl } from './constants'; -const CloudPage = function CloudPage(): ReactNode { +const CloudPage = (): ReactNode => { const t = useTranslation(); + const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); const cloudRoute = useRoute('cloud'); @@ -37,13 +38,12 @@ const CloudPage = function CloudPage(): ReactNode { const token = useQueryStringParameter('token'); const finishOAuthAuthorization = useMethod('cloud:finishOAuthAuthorization'); - const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); + const connectWorkspace = useMethod('cloud:connectWorkspace'); + const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); const reload = useMutableCallback(() => result.refetch()); - const connectWorkspace = useMethod('cloud:connectWorkspace'); - useEffect(() => { const acceptOAuthAuthorization = async (): Promise => { if (page !== 'oauth-callback') { @@ -62,7 +62,7 @@ const CloudPage = function CloudPage(): ReactNode { try { await finishOAuthAuthorization(code, state); - } catch (error: unknown) { + } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } finally { cloudRoute.push(); @@ -72,8 +72,6 @@ const CloudPage = function CloudPage(): ReactNode { acceptOAuthAuthorization(); }, [errorCode, code, state, page, dispatchToastMessage, t, cloudRoute, finishOAuthAuthorization]); - const setModal = useSetModal(); - useEffect(() => { const acceptWorkspaceToken = async (): Promise => { try { @@ -86,7 +84,7 @@ const CloudPage = function CloudPage(): ReactNode { dispatchToastMessage({ type: 'success', message: t('Connected') }); } - } catch (error: unknown) { + } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } finally { reload(); diff --git a/apps/meteor/client/views/admin/routes.tsx b/apps/meteor/client/views/admin/routes.tsx index 7a57e95727e5..6214dfaf9cb4 100644 --- a/apps/meteor/client/views/admin/routes.tsx +++ b/apps/meteor/client/views/admin/routes.tsx @@ -13,16 +13,6 @@ registerAdminRoute('/custom-sounds/:context?/:id?', { component: lazy(() => import('./customSounds/CustomSoundsRoute')), }); -registerAdminRoute('/apps/what-is-it', { - name: 'admin-apps-disabled', - component: lazy(() => import('./apps/AppsWhatIsIt')), -}); - -registerAdminRoute('/marketplace/:context?/:page?/:id?/:version?/:tab?', { - name: 'admin-marketplace', - component: lazy(() => import('./apps/AppsRoute')), -}); - registerAdminRoute('/info', { name: 'admin-info', component: lazy(() => import('./info/InformationRoute')), diff --git a/apps/meteor/client/views/admin/sidebarItems.ts b/apps/meteor/client/views/admin/sidebarItems.ts index 125592845965..eafe3b4872d6 100644 --- a/apps/meteor/client/views/admin/sidebarItems.ts +++ b/apps/meteor/client/views/admin/sidebarItems.ts @@ -61,12 +61,6 @@ export const { i18nLabel: 'Federation Dashboard', permissionGranted: (): boolean => hasPermission('view-federation-data'), }, - { - icon: 'cube', - href: 'admin-marketplace', - i18nLabel: 'Apps', - permissionGranted: (): boolean => hasPermission('manage-apps'), - }, { icon: 'mail', href: 'admin-email-inboxes', diff --git a/apps/meteor/client/views/admin/apps/AccordionLoading.tsx b/apps/meteor/client/views/marketplace/AccordionLoading.tsx similarity index 100% rename from apps/meteor/client/views/admin/apps/AccordionLoading.tsx rename to apps/meteor/client/views/marketplace/AccordionLoading.tsx diff --git a/apps/meteor/client/views/admin/apps/AppDetailsPage/AppDetailsPage.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx similarity index 63% rename from apps/meteor/client/views/admin/apps/AppDetailsPage/AppDetailsPage.tsx rename to apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx index 2c135e491fb5..adf4116bdf6b 100644 --- a/apps/meteor/client/views/admin/apps/AppDetailsPage/AppDetailsPage.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx @@ -1,22 +1,31 @@ import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import type { App } from '@rocket.chat/core-typings'; -import { Button, ButtonGroup, Box, Throbber, Tabs } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, Box, Throbber } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useTranslation, useCurrentRoute, useRoute, useRouteParameter, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { + useTranslation, + useCurrentRoute, + useRoute, + useRouteParameter, + useToastMessageDispatch, + usePermission, +} from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState, useCallback, useRef } from 'react'; -import type { ISettings } from '../../../../../app/apps/client/@types/IOrchestrator'; -import { Apps } from '../../../../../app/apps/client/orchestrator'; -import Page from '../../../../components/Page'; +import type { ISettings } from '../../../../ee/client/apps/@types/IOrchestrator'; +import { Apps } from '../../../../ee/client/apps/orchestrator'; +import Page from '../../../components/Page'; import { handleAPIError } from '../helpers'; import { useAppInfo } from '../hooks/useAppInfo'; import AppDetailsPageHeader from './AppDetailsPageHeader'; import AppDetailsPageLoading from './AppDetailsPageLoading'; +import AppDetailsPageTabs from './AppDetailsPageTabs'; import AppDetails from './tabs/AppDetails'; import AppLogs from './tabs/AppLogs'; import AppReleases from './tabs/AppReleases'; -import AppSecurity from './tabs/AppSecurity'; +import AppRequests from './tabs/AppRequests/AppRequests'; +import AppSecurity from './tabs/AppSecurity/AppSecurity'; import AppSettings from './tabs/AppSettings'; const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { @@ -28,6 +37,7 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { const settingsRef = useRef>({}); const appData = useAppInfo(id); + const isAdminUser = usePermission('manage-apps'); const [currentRouteName] = useCurrentRoute(); if (!currentRouteName) { @@ -35,7 +45,6 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { } const router = useRoute(currentRouteName); - const [, urlParams] = useCurrentRoute(); const tab = useRouteParameter('tab'); const context = useRouteParameter('context'); @@ -44,7 +53,7 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { }); const { installed, settings, privacyPolicySummary, permissions, tosLink, privacyLink, marketplace, name } = appData || {}; - const isSecurityVisible = privacyPolicySummary || permissions || tosLink || privacyLink; + const isSecurityVisible = Boolean(privacyPolicySummary || permissions || tosLink || privacyLink); const saveAppSettings = useCallback(async () => { const { current } = settingsRef; @@ -65,15 +74,11 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { setIsSaving(false); }, [dispatchToastMessage, id, name, settings]); - const handleTabClick = (tab: 'details' | 'security' | 'releases' | 'settings' | 'logs'): void => { - router.replace({ ...urlParams, tab }); - }; - return ( - + - {installed && ( + {installed && isAdminUser && (