import React, { useEffect, useState } from "react"; import { Repo } from "../../gen/ts/v1/config_pb"; import { Col, Empty, Flex, Row, Spin, TabsProps, Tabs, Tooltip, Typography } from "antd"; import { useRecoilValue } from "recoil"; import { configState } from "../state/config"; import { useAlertApi } from "../components/Alerts"; import { OperationList } from "../components/OperationList"; import { OperationTree } from "../components/OperationTree"; import { MAX_OPERATION_HISTORY, STATS_OPERATION_HISTORY } from "../constants"; import { GetOperationsRequest } from "../../gen/ts/v1/service_pb"; import { getOperations } from "../state/oplog"; import { RepoStats } from "../../gen/ts/v1/restic_pb"; import { formatBytes, formatTime } from "../lib/formatting"; import { Operation } from "../../gen/ts/v1/operations_pb"; import { backrestService } from "../api"; import { StringValue } from "@bufbuild/protobuf"; import { SpinButton } from "../components/SpinButton"; export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => { const alertsApi = useAlertApi()!; const [loading, setLoading] = useState(true); const [statsOperation, setStatsOperation] = useState(null); useEffect(() => { setLoading(true); setStatsOperation(null); getOperations(new GetOperationsRequest({ repoId: repo.id!, lastN: BigInt(STATS_OPERATION_HISTORY) })).then((operations) => { for (const op of operations) { if (op.op.case === "operationStats") { const stats = op.op.value.stats; if (stats) { setStatsOperation(op); } } } }).catch((e) => { console.error(e); }).finally(() => { setLoading(false); }); }, [repo.id]); // Task handlers const handleIndexNow = async () => { await backrestService.indexSnapshots(new StringValue({ value: repo.id! })); } // Gracefully handle deletions by checking if the plan is still in the config. const config = useRecoilValue(configState); let repoInConfig = config.repos?.find((p) => p.id === repo.id); if (!repoInConfig) { return

Repo was deleted.

; } repo = repoInConfig; if (loading) { return ; } const items = [ { key: "1", label: "Stats", children: ( <> {statsOperation === null ? : <>

Repo stats computed on {formatTime(Number(statsOperation.unixTimeStartMs))}

{statsOperation.op.case === "operationStats" && } Stats are refreshed periodically in the background as new data is added (e.g. every 10GB added or every 50 operations). } ), destroyInactiveTabPane: true, }, { key: "2", label: "Tree View", children: ( <>

Browse Backups

), destroyInactiveTabPane: true, }, { key: "3", label: "Operation List", children: ( <>

Backup Action History

), destroyInactiveTabPane: true, }, ] return ( <> {repo.id} Index Snapshots ); }; const StatsTable = ({ stats }: { stats: RepoStats }) => { return

Total Size:

Total Size Uncompressed:

Blob Count:

Snapshot Count:

Compression Ratio:

{formatBytes(Number(stats.totalSize))}

{formatBytes(Number(stats.totalUncompressedSize))}

{Number(stats.totalBlobCount)} blobs

{Number(stats.snapshotCount)} snapshots

{Math.round(stats.compressionRatio * 1000) / 1000}

}