|
|
@@ -3,18 +3,56 @@ import React, { useCallback, useEffect, useState } from 'react';
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
|
|
import { useGenerateTransferKeyWithThrottle } from '~/client/services/g2g-transfer';
|
|
|
-import { toastError } from '~/client/util/apiNotification';
|
|
|
+import { toastError, toastSuccess } from '~/client/util/apiNotification';
|
|
|
import { apiv3Get, apiv3Post } from '~/client/util/apiv3-client';
|
|
|
import { useAdminSocket } from '~/stores/socket-io';
|
|
|
|
|
|
import CustomCopyToClipBoard from '../Common/CustomCopyToClipBoard';
|
|
|
|
|
|
import G2GDataTransferExportForm from './G2GDataTransferExportForm';
|
|
|
+import G2GDataTransferStatusIcon from './G2GDataTransferStatusIcon';
|
|
|
|
|
|
const IGNORED_COLLECTION_NAMES = [
|
|
|
'sessions', 'rlflx', 'activities', 'attachmentFiles.files', 'attachmentFiles.chunks',
|
|
|
];
|
|
|
|
|
|
+/**
|
|
|
+ * Get the union type of all the values in an object, array or array-like type `T`
|
|
|
+ * @see {@link https://github.com/piotrwitek/utility-types/blob/df2502ef504c4ba8bd9de81a45baef112b7921d0/src/mapped-types.ts#L589-L597}
|
|
|
+ */
|
|
|
+type ValuesType<
|
|
|
+ T extends ReadonlyArray<any> | ArrayLike<any> | Record<any, any>
|
|
|
+> = T extends ReadonlyArray<any>
|
|
|
+ ? T[number]
|
|
|
+ : T extends ArrayLike<any>
|
|
|
+ ? T[number]
|
|
|
+ : T extends object
|
|
|
+ ? T[keyof T]
|
|
|
+ : never;
|
|
|
+
|
|
|
+/**
|
|
|
+ * G2G transfer progress status master
|
|
|
+ */
|
|
|
+const G2G_PROGRESS_STATUS = {
|
|
|
+ PENDING: 'PENDING',
|
|
|
+ IN_PROGRESS: 'IN_PROGRESS',
|
|
|
+ COMPLETED: 'COMPLETED',
|
|
|
+ ERROR: 'ERROR',
|
|
|
+} as const;
|
|
|
+
|
|
|
+/**
|
|
|
+ * G2G transfer progress status
|
|
|
+ */
|
|
|
+type G2GProgressStatus = ValuesType<typeof G2G_PROGRESS_STATUS>
|
|
|
+
|
|
|
+/**
|
|
|
+ * G2G transfer progress
|
|
|
+ */
|
|
|
+interface G2GProgress {
|
|
|
+ mongo: G2GProgressStatus;
|
|
|
+ attachments: G2GProgressStatus;
|
|
|
+}
|
|
|
+
|
|
|
const G2GDataTransfer = (): JSX.Element => {
|
|
|
const { data: socket } = useAdminSocket();
|
|
|
const { t } = useTranslation();
|
|
|
@@ -25,7 +63,10 @@ const G2GDataTransfer = (): JSX.Element => {
|
|
|
const [optionsMap, setOptionsMap] = useState<any>({});
|
|
|
const [isShowExportForm, setShowExportForm] = useState(false);
|
|
|
const [isTransferring, setTransferring] = useState(false);
|
|
|
- const [statusMessage, setStatusMessage] = useState<string | undefined>(undefined);
|
|
|
+ const [g2gProgress, setG2GProgress] = useState<G2GProgress>({
|
|
|
+ mongo: G2G_PROGRESS_STATUS.PENDING,
|
|
|
+ attachments: G2G_PROGRESS_STATUS.PENDING,
|
|
|
+ });
|
|
|
|
|
|
const updateSelectedCollections = (newSelectedCollections: Set<string>) => {
|
|
|
setSelectedCollections(newSelectedCollections);
|
|
|
@@ -56,36 +97,25 @@ const G2GDataTransfer = (): JSX.Element => {
|
|
|
|
|
|
const setupWebsocketEventHandler = useCallback(() => {
|
|
|
if (socket != null) {
|
|
|
- socket.on('admin:onStartTransferMongoData', () => {
|
|
|
- setTransferring(true);
|
|
|
- setStatusMessage(t('Transferring DB data ...'));
|
|
|
- });
|
|
|
+ socket.on('admin:g2gProgress', (g2gProgress: G2GProgress) => {
|
|
|
+ setG2GProgress(g2gProgress);
|
|
|
|
|
|
- socket.on('admin:onStartTransferAttachments', () => {
|
|
|
- setStatusMessage(t('Transferring attachment files ...'));
|
|
|
+ if (g2gProgress.mongo === G2G_PROGRESS_STATUS.COMPLETED && g2gProgress.attachments === G2G_PROGRESS_STATUS.COMPLETED) {
|
|
|
+ toastSuccess(t('admin:g2g:transfer_success'));
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
- socket.on('admin:onFinishTransfer', () => {
|
|
|
+ socket.on('admin:g2gError', ({ key }) => {
|
|
|
setTransferring(false);
|
|
|
- setStatusMessage(t('Successfully transferred GROWI. Now you can use new GROWI !'));
|
|
|
- });
|
|
|
-
|
|
|
- socket.on('admin:onG2gError', ({ key }) => {
|
|
|
- setTransferring(false);
|
|
|
- setStatusMessage(t(key));
|
|
|
+ toastError(t(key));
|
|
|
});
|
|
|
}
|
|
|
- }, [socket, t]);
|
|
|
+ }, [socket, t, setTransferring, setG2GProgress]);
|
|
|
|
|
|
const cleanUpWebsocketEventHandler = useCallback(() => {
|
|
|
if (socket != null) {
|
|
|
- socket.off('admin:onStartTransferMongoData');
|
|
|
-
|
|
|
- socket.off('admin:onStartTransferAttachments');
|
|
|
-
|
|
|
- socket.off('admin:onFinishTransfer');
|
|
|
-
|
|
|
- socket.off('admin:onG2gError');
|
|
|
+ socket.off('admin:g2gProgress');
|
|
|
+ socket.off('admin:g2gError');
|
|
|
}
|
|
|
}, [socket]);
|
|
|
|
|
|
@@ -97,6 +127,7 @@ const G2GDataTransfer = (): JSX.Element => {
|
|
|
|
|
|
const startTransfer = useCallback(async(e) => {
|
|
|
e.preventDefault();
|
|
|
+ setTransferring(true);
|
|
|
|
|
|
try {
|
|
|
await apiv3Post('/g2g-transfer/transfer', {
|
|
|
@@ -108,13 +139,16 @@ const G2GDataTransfer = (): JSX.Element => {
|
|
|
catch (errs) {
|
|
|
toastError(errs);
|
|
|
}
|
|
|
- }, [startTransferKey, selectedCollections, optionsMap]);
|
|
|
+ }, [setTransferring, startTransferKey, selectedCollections, optionsMap]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
setCollectionsAndSelectedCollections();
|
|
|
-
|
|
|
setupWebsocketEventHandler();
|
|
|
- }, [setCollectionsAndSelectedCollections, setupWebsocketEventHandler]);
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ cleanUpWebsocketEventHandler();
|
|
|
+ };
|
|
|
+ }, [setCollectionsAndSelectedCollections, setupWebsocketEventHandler, cleanUpWebsocketEventHandler]);
|
|
|
|
|
|
return (
|
|
|
<div data-testid="admin-export-archive-data">
|
|
|
@@ -153,16 +187,15 @@ const G2GDataTransfer = (): JSX.Element => {
|
|
|
</div>
|
|
|
</form>
|
|
|
|
|
|
-
|
|
|
- {statusMessage != null && (
|
|
|
- <>
|
|
|
- <div className='alert alert-info d-flex align-items-center'>
|
|
|
- {isTransferring && (
|
|
|
- <i className="fa fa-2x fa-spinner fa-pulse mr-2"></i>
|
|
|
- )}
|
|
|
- <p className="mb-0">{statusMessage}</p>
|
|
|
+ {isTransferring && (
|
|
|
+ <div className='border rounded p-4'>
|
|
|
+ <div>
|
|
|
+ <G2GDataTransferStatusIcon className='mr-2 mb-2' status={g2gProgress.mongo} /> MongoDB
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <G2GDataTransferStatusIcon className='mr-2' status={g2gProgress.attachments} /> Attachments
|
|
|
</div>
|
|
|
- </>
|
|
|
+ </div>
|
|
|
)}
|
|
|
|
|
|
<h2 className="border-bottom mt-5">{t('admin:g2g_data_transfer.transfer_data_to_this_growi')}</h2>
|