import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { Button, Col, Form, Row } from "react-bootstrap"; import { mutateMigrateHashNaming, mutateMetadataExport, mutateBackupDatabase, mutateMetadataImport, mutateMetadataClean, mutateAnonymiseDatabase, mutateMigrateSceneScreenshots, mutateMigrateBlobs, mutateOptimiseDatabase, mutateCleanGenerated, } from "src/core/StashService"; import { useToast } from "src/hooks/Toast"; import downloadFile from "src/utils/download"; import { ModalComponent } from "src/components/Shared/Modal"; import { ImportDialog } from "./ImportDialog"; import * as GQL from "src/core/generated-graphql"; import { SettingSection } from "../SettingSection"; import { BooleanSetting, Setting } from "../Inputs"; import { ManualLink } from "src/components/Help/context"; import { Icon } from "src/components/Shared/Icon"; import { ConfigurationContext } from "src/hooks/Config"; import { FolderSelect } from "src/components/Shared/FolderSelect/FolderSelect"; import { faMinus, faPlus, faQuestionCircle, faTrashAlt, } from "@fortawesome/free-solid-svg-icons"; import { CleanGeneratedDialog } from "./CleanGeneratedDialog"; interface ICleanDialog { pathSelection?: boolean; dryRun: boolean; onClose: (paths?: string[]) => void; } const CleanDialog: React.FC = ({ pathSelection = false, dryRun, onClose, }) => { const intl = useIntl(); const { configuration } = React.useContext(ConfigurationContext); const libraryPaths = configuration?.general.stashes.map((s) => s.path); const [paths, setPaths] = useState([]); const [currentDirectory, setCurrentDirectory] = useState(""); function removePath(p: string) { setPaths(paths.filter((path) => path !== p)); } function addPath(p: string) { if (p && !paths.includes(p)) { setPaths(paths.concat(p)); } } let msg; if (dryRun) { msg = (

{intl.formatMessage({ id: "actions.tasks.dry_mode_selected" })}

); } else { msg = (

{intl.formatMessage({ id: "actions.tasks.clean_confirm_message" })}

); } return ( onClose(paths), }} cancel={{ onClick: () => onClose() }} >
{paths.map((p) => ( {p} ))} {pathSelection ? ( addPath(currentDirectory)} > } /> ) : undefined}
{msg}
); }; interface ICleanOptions { options: GQL.CleanMetadataInput; setOptions: (s: GQL.CleanMetadataInput) => void; } const CleanOptions: React.FC = ({ options, setOptions: setOptionsState, }) => { function setOptions(input: Partial) { setOptionsState({ ...options, ...input }); } return ( <> setOptions({ dryRun: v })} /> ); }; interface IDataManagementTasks { setIsBackupRunning: (v: boolean) => void; setIsAnonymiseRunning: (v: boolean) => void; } export const DataManagementTasks: React.FC = ({ setIsBackupRunning, setIsAnonymiseRunning, }) => { const intl = useIntl(); const Toast = useToast(); const [dialogOpen, setDialogOpenState] = useState({ importAlert: false, import: false, clean: false, cleanAlert: false, cleanGenerated: false, }); const [cleanOptions, setCleanOptions] = useState({ dryRun: false, }); const [migrateBlobsOptions, setMigrateBlobsOptions] = useState({ deleteOld: true, }); const [migrateSceneScreenshotsOptions, setMigrateSceneScreenshotsOptions] = useState({ deleteFiles: false, overwriteExisting: false, }); type DialogOpenState = typeof dialogOpen; function setDialogOpen(s: Partial) { setDialogOpenState((v) => { return { ...v, ...s }; }); } async function onImport() { setDialogOpen({ importAlert: false }); try { await mutateMetadataImport(); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.import" }) } ) ); } catch (e) { Toast.error(e); } } function renderImportAlert() { return ( setDialogOpen({ importAlert: false }) }} >

{intl.formatMessage({ id: "actions.tasks.import_warning" })}

); } function renderImportDialog() { if (!dialogOpen.import) { return; } return setDialogOpen({ import: false })} />; } async function onClean(paths?: string[]) { try { await mutateMetadataClean({ ...cleanOptions, paths, }); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.clean" }) } ) ); } catch (e) { Toast.error(e); } finally { setDialogOpen({ clean: false }); } } async function onCleanGenerated(options: GQL.CleanGeneratedInput) { try { await mutateCleanGenerated({ ...options, }); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.clean_generated", }), } ) ); } catch (e) { Toast.error(e); } } async function onMigrateHashNaming() { try { await mutateMigrateHashNaming(); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.hash_migration", }), } ) ); } catch (err) { Toast.error(err); } } async function onMigrateSceneScreenshots() { try { await mutateMigrateSceneScreenshots(migrateSceneScreenshotsOptions); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.migrate_scene_screenshots", }), } ) ); } catch (err) { Toast.error(err); } } async function onMigrateBlobs() { try { await mutateMigrateBlobs(migrateBlobsOptions); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.migrate_blobs", }), } ) ); } catch (err) { Toast.error(err); } } async function onExport() { try { await mutateMetadataExport(); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.export" }) } ) ); } catch (err) { Toast.error(err); } } async function onBackup(download?: boolean) { try { setIsBackupRunning(true); const ret = await mutateBackupDatabase({ download, }); // download the result if (download && ret.data && ret.data.backupDatabase) { const link = ret.data.backupDatabase; downloadFile(link); } } catch (e) { Toast.error(e); } finally { setIsBackupRunning(false); } } async function onOptimiseDatabase() { try { await mutateOptimiseDatabase(); Toast.success( intl.formatMessage( { id: "config.tasks.added_job_to_queue" }, { operation_name: intl.formatMessage({ id: "actions.optimise_database", }), } ) ); } catch (e) { Toast.error(e); } } async function onAnonymise(download?: boolean) { try { setIsAnonymiseRunning(true); const ret = await mutateAnonymiseDatabase({ download, }); // download the result if (download && ret.data && ret.data.anonymiseDatabase) { const link = ret.data.anonymiseDatabase; downloadFile(link); } } catch (e) { Toast.error(e); } finally { setIsAnonymiseRunning(false); } } return ( {renderImportAlert()} {renderImportDialog()} {dialogOpen.cleanAlert || dialogOpen.clean ? ( { // undefined means cancelled if (p !== undefined) { if (dialogOpen.cleanAlert) { // don't provide paths onClean(); } else { onClean(p); } } setDialogOpen({ clean: false, cleanAlert: false, }); }} /> ) : ( dialogOpen.clean )} {dialogOpen.cleanGenerated && ( { if (options) { onCleanGenerated(options); } setDialogOpen({ cleanGenerated: false }); }} /> )}
} subHeadingID="config.tasks.cleanup_desc" > setCleanOptions(o)} />
} subHeadingID="config.tasks.clean_generated.description" >

} >
[origFilename].sqlite.[schemaVersion].[YYYYMMDD_HHMMSS] ), } )} > [origFilename].anonymous.sqlite.[schemaVersion].[YYYYMMDD_HHMMSS] ), } )} >
setMigrateBlobsOptions({ ...migrateBlobsOptions, deleteOld: v }) } />
setMigrateSceneScreenshotsOptions({ ...migrateSceneScreenshotsOptions, overwriteExisting: v, }) } /> setMigrateSceneScreenshotsOptions({ ...migrateSceneScreenshotsOptions, deleteFiles: v, }) } />
); };