فهرست منبع

if an export using the same filter is currently in progress you can choose whether to retry or confirm

ryosei-f 1 ماه پیش
والد
کامیت
deb87f0996

+ 3 - 0
apps/app/public/static/locales/en_US/admin.json

@@ -864,6 +864,9 @@
     "export_audit_log": "Export Audit Log",
     "export_audit_log": "Export Audit Log",
     "export_requested": "Export request accepted. You will be notified when the export is complete.",
     "export_requested": "Export request accepted. You will be notified when the export is complete.",
     "export_failed": "Failed to start export",
     "export_failed": "Failed to start export",
+    "duplicate_export_confirm": "An export with the same conditions is already in progress. Do you want to restart it?",
+    "restart_export": "Restart Export",
+    "confirm_export": "Confirm Export",
     "docs_url": {
     "docs_url": {
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
     }
     }

+ 3 - 0
apps/app/public/static/locales/fr_FR/admin.json

@@ -863,6 +863,9 @@
     "export_audit_log": "Exporter le journal d'audit",
     "export_audit_log": "Exporter le journal d'audit",
     "export_requested": "Demande d'exportation acceptée. Vous serez averti lorsque l'exportation sera terminée.",
     "export_requested": "Demande d'exportation acceptée. Vous serez averti lorsque l'exportation sera terminée.",
     "export_failed": "Échec du démarrage de l'exportation",
     "export_failed": "Échec du démarrage de l'exportation",
+    "duplicate_export_confirm": "Une exportation avec les mêmes conditions est déjà en cours. Voulez-vous la redémarrer ?",
+    "restart_export": "Redémarrer l'exportation",
+    "confirm_export": "Confirmer l'exportation",
     "docs_url": {
     "docs_url": {
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
     }
     }

+ 3 - 0
apps/app/public/static/locales/ja_JP/admin.json

@@ -873,6 +873,9 @@
     "export_audit_log": "監査ログのエクスポート",
     "export_audit_log": "監査ログのエクスポート",
     "export_requested": "エクスポートリクエストを受け付けました。完了後に通知されます。",
     "export_requested": "エクスポートリクエストを受け付けました。完了後に通知されます。",
     "export_failed": "エクスポートの開始に失敗しました",
     "export_failed": "エクスポートの開始に失敗しました",
+    "duplicate_export_confirm": "同じ条件のエクスポートが進行中です。やり直しますか?",
+    "restart_export": "やり直す",
+    "confirm_export": "エクスポートの確認",
     "docs_url": {
     "docs_url": {
       "log_type": "https://docs.growi.org/ja/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
       "log_type": "https://docs.growi.org/ja/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
     }
     }

+ 3 - 0
apps/app/public/static/locales/ko_KR/admin.json

@@ -864,6 +864,9 @@
     "export_audit_log": "감사 로그 내보내기",
     "export_audit_log": "감사 로그 내보내기",
     "export_requested": "내보내기 요청이 접수되었습니다. 내보내기가 완료되면 알림을 받게 됩니다.",
     "export_requested": "내보내기 요청이 접수되었습니다. 내보내기가 완료되면 알림을 받게 됩니다.",
     "export_failed": "내보내기 시작에 실패했습니다",
     "export_failed": "내보내기 시작에 실패했습니다",
+    "duplicate_export_confirm": "동일한 조건의 내보내기가 이미 진행 중입니다. 다시 시작하시겠습니까?",
+    "restart_export": "내보내기 다시 시작",
+    "confirm_export": "내보내기 확인",
     "docs_url": {
     "docs_url": {
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
     }
     }

+ 3 - 0
apps/app/public/static/locales/zh_CN/admin.json

@@ -873,6 +873,9 @@
     "export_audit_log": "导出审核日志",
     "export_audit_log": "导出审核日志",
     "export_requested": "导出请求已接受。导出完成后将通知您。",
     "export_requested": "导出请求已接受。导出完成后将通知您。",
     "export_failed": "导出启动失败",
     "export_failed": "导出启动失败",
+    "duplicate_export_confirm": "已有相同条件的导出正在进行中。是否要重新启动它?",
+    "restart_export": "重新启动导出",
+    "confirm_export": "确认导出",
     "docs_url": {
     "docs_url": {
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
       "log_type": "https://docs.growi.org/en/admin-guide/admin-cookbook/audit-log-setup.html#log-types"
     }
     }

+ 81 - 21
apps/app/src/client/components/Admin/AuditLog/AuditLogExportModal.tsx

@@ -39,6 +39,8 @@ const AuditLogExportModalSubstance = ({
       ),
       ),
   );
   );
   const [isExporting, setIsExporting] = useState<boolean>(false);
   const [isExporting, setIsExporting] = useState<boolean>(false);
+  const [isDuplicateConfirmOpen, setIsDuplicateConfirmOpen] =
+    useState<boolean>(false);
 
 
   const datePickerChangedHandler = useCallback((dateList: Date[] | null[]) => {
   const datePickerChangedHandler = useCallback((dateList: Date[] | null[]) => {
     setStartDate(dateList[0]);
     setStartDate(dateList[0]);
@@ -73,31 +75,61 @@ const AuditLogExportModalSubstance = ({
     setSelectedUsernames(usernames);
     setSelectedUsernames(usernames);
   }, []);
   }, []);
 
 
+  const buildFilters = useCallback(() => {
+    const selectedActionList = Array.from(actionMap.entries())
+      .filter((v) => v[1])
+      .map((v) => v[0]);
+
+    const filters: {
+      actions?: SupportedActionType[];
+      dateFrom?: Date;
+      dateTo?: Date;
+      // TODO: Add users filter after implementing username-to-userId conversion
+    } = {};
+
+    if (selectedActionList.length > 0) {
+      filters.actions = selectedActionList;
+    }
+    if (startDate != null) {
+      filters.dateFrom = startDate;
+    }
+    if (endDate != null) {
+      filters.dateTo = endDate;
+    }
+
+    return filters;
+  }, [actionMap, startDate, endDate]);
+
   const exportHandler = useCallback(async () => {
   const exportHandler = useCallback(async () => {
     setIsExporting(true);
     setIsExporting(true);
     try {
     try {
-      const selectedActionList = Array.from(actionMap.entries())
-        .filter((v) => v[1])
-        .map((v) => v[0]);
-
-      const filters: {
-        actions?: SupportedActionType[];
-        dateFrom?: Date;
-        dateTo?: Date;
-        // TODO: Add users filter after implementing username-to-userId conversion
-      } = {};
-
-      if (selectedActionList.length > 0) {
-        filters.actions = selectedActionList;
-      }
-      if (startDate != null) {
-        filters.dateFrom = startDate;
-      }
-      if (endDate != null) {
-        filters.dateTo = endDate;
+      const filters = buildFilters();
+      await apiv3Post('/audit-log-bulk-export', { filters });
+      toastSuccess(t('audit_log_management.export_requested'));
+      onClose();
+    } catch (errs) {
+      const isDuplicate =
+        Array.isArray(errs) &&
+        errs.some(
+          (e) => e.code === 'audit_log_bulk_export.duplicate_export_job_error',
+        );
+
+      if (isDuplicate) {
+        setIsDuplicateConfirmOpen(true);
+      } else {
+        toastError(t('audit_log_management.export_failed'));
       }
       }
+    } finally {
+      setIsExporting(false);
+    }
+  }, [buildFilters, t, onClose]);
 
 
-      await apiv3Post('/audit-log-bulk-export', { filters });
+  const restartExportHandler = useCallback(async () => {
+    setIsDuplicateConfirmOpen(false);
+    setIsExporting(true);
+    try {
+      const filters = buildFilters();
+      await apiv3Post('/audit-log-bulk-export', { filters, restartJob: true });
       toastSuccess(t('audit_log_management.export_requested'));
       toastSuccess(t('audit_log_management.export_requested'));
       onClose();
       onClose();
     } catch {
     } catch {
@@ -105,7 +137,7 @@ const AuditLogExportModalSubstance = ({
     } finally {
     } finally {
       setIsExporting(false);
       setIsExporting(false);
     }
     }
-  }, [actionMap, startDate, endDate, t, onClose]);
+  }, [buildFilters, t, onClose]);
 
 
   return (
   return (
     <>
     <>
@@ -161,6 +193,34 @@ const AuditLogExportModalSubstance = ({
           {t('audit_log_management.export')}
           {t('audit_log_management.export')}
         </button>
         </button>
       </ModalFooter>
       </ModalFooter>
+
+      <Modal
+        isOpen={isDuplicateConfirmOpen}
+        toggle={() => setIsDuplicateConfirmOpen(false)}
+      >
+        <ModalHeader tag="h4" toggle={() => setIsDuplicateConfirmOpen(false)}>
+          {t('audit_log_management.confirm_export')}
+        </ModalHeader>
+        <ModalBody>
+          {t('audit_log_management.duplicate_export_confirm')}
+        </ModalBody>
+        <ModalFooter>
+          <button
+            type="button"
+            className="btn btn-outline-secondary"
+            onClick={() => setIsDuplicateConfirmOpen(false)}
+          >
+            {t('export_management.cancel')}
+          </button>
+          <button
+            type="button"
+            className="btn btn-primary"
+            onClick={restartExportHandler}
+          >
+            {t('audit_log_management.restart_export')}
+          </button>
+        </ModalFooter>
+      </Modal>
     </>
     </>
   );
   );
 };
 };