RocketChat/Rocket.Chat

View on GitHub
apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts

Summary

Maintainability
B
5 hrs
Test Coverage
import type { Cloud, Serialized } from '@rocket.chat/core-typings';
import { DuplicatedLicenseError } from '@rocket.chat/license';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { v, compile } from 'suretype';

import { callbacks } from '../../../../../lib/callbacks';
import { CloudWorkspaceAccessError } from '../../../../../lib/errors/CloudWorkspaceAccessError';
import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError';
import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError';
import { SystemLogger } from '../../../../../server/lib/logger/system';
import { settings } from '../../../../settings/server';
import { buildWorkspaceRegistrationData } from '../buildRegistrationData';
import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken';
import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus';

const workspaceSyncPayloadSchema = v.object({
    workspaceId: v.string().required(),
    publicKey: v.string(),
    license: v.string().required(),
});

const assertWorkspaceSyncPayload = compile(workspaceSyncPayloadSchema);

const fetchWorkspaceSyncPayload = async ({
    token,
    data,
}: {
    token: string;
    data: Cloud.WorkspaceSyncRequestPayload;
}): Promise<Serialized<Cloud.WorkspaceSyncResponse>> => {
    const workspaceRegistrationClientUri = settings.get<string>('Cloud_Workspace_Registration_Client_Uri');
    const response = await fetch(`${workspaceRegistrationClientUri}/sync`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${token}`,
        },
        body: data,
    });

    if (!response.ok) {
        try {
            const { error } = await response.json();
            throw new CloudWorkspaceConnectionError(`Failed to connect to Rocket.Chat Cloud: ${error}`);
        } catch (error) {
            throw new CloudWorkspaceConnectionError(`Failed to connect to Rocket.Chat Cloud: ${response.statusText}`);
        }
    }

    const payload = await response.json();

    assertWorkspaceSyncPayload(payload);

    return payload;
};

export async function syncCloudData() {
    try {
        const { workspaceRegistered } = await retrieveRegistrationStatus();
        if (!workspaceRegistered) {
            throw new CloudWorkspaceRegistrationError('Workspace is not registered');
        }

        const token = await getWorkspaceAccessToken(true);
        if (!token) {
            throw new CloudWorkspaceAccessTokenEmptyError();
        }

        const workspaceRegistrationData = await buildWorkspaceRegistrationData(undefined);

        const { license, removeLicense = false } = await fetchWorkspaceSyncPayload({
            token,
            data: workspaceRegistrationData,
        });

        if (removeLicense) {
            await callbacks.run('workspaceLicenseRemoved');
        } else {
            await callbacks.run('workspaceLicenseChanged', license);
        }

        SystemLogger.info({
            msg: 'Synced with Rocket.Chat Cloud',
            function: 'syncCloudData',
        });

        return true;
    } catch (err) {
        /**
         * If some of CloudWorkspaceAccessError and CloudWorkspaceRegistrationError happens, makes no sense to run the legacySyncWorkspace
         * because it will fail too.
         * The DuplicatedLicenseError license error is also ignored because it is not a problem. the Cloud is allowed to send the same license twice.
         */
        switch (true) {
            case err instanceof DuplicatedLicenseError:
                return;
            case err instanceof CloudWorkspaceAccessError:
            case err instanceof CloudWorkspaceRegistrationError:
            case err instanceof CloudWorkspaceAccessTokenEmptyError:
                SystemLogger.info({
                    msg: 'Failed to sync with Rocket.Chat Cloud',
                    function: 'syncCloudData',
                    err,
                });
                break;

            default:
                SystemLogger.error({
                    msg: 'Failed to sync with Rocket.Chat Cloud',
                    function: 'syncCloudData',
                    err,
                });
        }
        throw err;
    }
}