feat(volumes): read only mount mode

This commit is contained in:
Nicolas Meienberger
2025-11-08 10:01:54 +01:00
parent 418369c4ad
commit f5339d3708
9 changed files with 137 additions and 51 deletions

View File

@@ -17,16 +17,20 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from "~/components/ui/alert-dialog";
import type { Snapshot } from "~/lib/types";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip";
import type { Snapshot, Volume } from "~/lib/types";
import { toast } from "sonner";
interface Props {
snapshot: Snapshot;
repositoryName: string;
volume?: Volume;
}
export const SnapshotFileBrowser = (props: Props) => {
const { snapshot, repositoryName } = props;
const { snapshot, repositoryName, volume } = props;
const isReadOnly = volume?.config && "readOnly" in volume.config && volume.config.readOnly === true;
const queryClient = useQueryClient();
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set());
@@ -195,11 +199,28 @@ export const SnapshotFileBrowser = (props: Props) => {
<CardDescription>{`Viewing snapshot from ${new Date(snapshot?.time ?? 0).toLocaleString()}`}</CardDescription>
</div>
{selectedPaths.size > 0 && (
<Button onClick={handleRestoreClick} variant="primary" size="sm" disabled={isRestoring}>
{isRestoring
? "Restoring..."
: `Restore ${selectedPaths.size} selected ${selectedPaths.size === 1 ? "item" : "items"}`}
</Button>
<Tooltip>
<TooltipTrigger asChild>
<span tabIndex={isReadOnly ? 0 : undefined}>
<Button
onClick={handleRestoreClick}
variant="primary"
size="sm"
disabled={isRestoring || isReadOnly}
>
{isRestoring
? "Restoring..."
: `Restore ${selectedPaths.size} selected ${selectedPaths.size === 1 ? "item" : "items"}`}
</Button>
</span>
</TooltipTrigger>
{isReadOnly && (
<TooltipContent className="text-center">
<p>Volume is mounted as read-only.</p>
<p>Please remount with read-only disabled to restore files.</p>
</TooltipContent>
)}
</Tooltip>
)}
</div>
</CardHeader>

View File

@@ -181,6 +181,7 @@ export default function ScheduleDetailsPage({ params, loaderData }: Route.Compon
key={selectedSnapshot?.short_id}
snapshot={selectedSnapshot}
repositoryName={schedule.repository.name}
volume={schedule.volume}
/>
)}
</div>