feat(ui): improve local repository path display and messaging

- Add shared client constants file with REPOSITORY_BASE
- Show effective local path in repository info tab
- Update host mount warning to clarify default path is safe when using recommended Docker setup
- Update README to clarify local repositories can use custom paths
This commit is contained in:
Jakub Trávník 2025-12-23 12:23:01 +01:00
parent 3a54f11b6d
commit cd9f5a9bbe
4 changed files with 22 additions and 5 deletions

View file

@ -145,7 +145,7 @@ Now, when adding a new volume in the Zerobyte web interface, you can select "Dir
A repository is where your backups will be securely stored encrypted. Zerobyte supports multiple storage backends for your backup repositories:
- **Local directories** - Store backups on local disk at `/var/lib/zerobyte/repositories/<repository-name>`
- **Local directories** - Store backups on local disk subfolder of `/var/lib/zerobyte/repositories/` or any other (mounted) path
- **S3-compatible storage** - Amazon S3, MinIO, Wasabi, DigitalOcean Spaces, etc.
- **Google Cloud Storage** - Google's cloud storage service
- **Azure Blob Storage** - Microsoft Azure storage

View file

@ -0,0 +1,2 @@
/** Default base path for local repositories */
export const REPOSITORY_BASE = "/var/lib/zerobyte/repositories";

View file

@ -1,6 +1,7 @@
import { useState } from "react";
import type { UseFormReturn } from "react-hook-form";
import { Check, Pencil, X, AlertTriangle } from "lucide-react";
import { REPOSITORY_BASE } from "~/client/lib/constants";
import { Button } from "../../../../components/ui/button";
import { FormItem, FormLabel, FormDescription } from "../../../../components/ui/form";
import { DirectoryBrowser } from "../../../../components/directory-browser";
@ -30,7 +31,7 @@ export const LocalRepositoryForm = ({ form }: Props) => {
<FormLabel>Repository Directory</FormLabel>
<div className="flex items-center gap-2">
<div className="flex-1 text-sm font-mono bg-muted px-3 py-2 rounded-md border">
{form.watch("path") || "/var/lib/zerobyte/repositories"}
{form.watch("path") || REPOSITORY_BASE}
</div>
<Button type="button" variant="outline" onClick={() => setShowPathWarning(true)} size="sm">
<Pencil className="h-4 w-4 mr-2" />
@ -53,8 +54,8 @@ export const LocalRepositoryForm = ({ form }: Props) => {
If the path is not a host mount, you will lose your repository data when the container restarts.
</p>
<p className="text-sm text-muted-foreground">
The default path <code className="bg-muted px-1 rounded">/var/lib/zerobyte/repositories</code> is
already mounted from the host and is safe to use.
The default path <code className="bg-muted px-1 rounded">{REPOSITORY_BASE}</code> is
safe to use if you followed the recommended Docker Compose setup.
</p>
</AlertDialogDescription>
</AlertDialogHeader>
@ -83,7 +84,7 @@ export const LocalRepositoryForm = ({ form }: Props) => {
<div className="py-4">
<DirectoryBrowser
onSelectPath={(path) => form.setValue("path", path)}
selectedPath={form.watch("path") || "/var/lib/zerobyte/repositories"}
selectedPath={form.watch("path") || REPOSITORY_BASE}
/>
</div>
<AlertDialogFooter>

View file

@ -18,6 +18,7 @@ import {
AlertDialogTitle,
} from "~/client/components/ui/alert-dialog";
import type { Repository } from "~/client/lib/types";
import { REPOSITORY_BASE } from "~/client/lib/constants";
import { updateRepositoryMutation } from "~/client/api-client/@tanstack/react-query.gen";
import type { CompressionMode } from "~/schemas/restic";
@ -25,6 +26,13 @@ type Props = {
repository: Repository;
};
const getEffectiveLocalPath = (repository: Repository): string | null => {
if (repository.type !== "local") return null;
const config = repository.config as { name: string; path?: string };
const basePath = config.path || REPOSITORY_BASE;
return `${basePath}/${config.name}`;
};
export const RepositoryInfoTabContent = ({ repository }: Props) => {
const [name, setName] = useState(repository.name);
const [compressionMode, setCompressionMode] = useState<CompressionMode>(
@ -113,6 +121,12 @@ export const RepositoryInfoTabContent = ({ repository }: Props) => {
<div className="text-sm font-medium text-muted-foreground">Status</div>
<p className="mt-1 text-sm">{repository.status || "unknown"}</p>
</div>
{getEffectiveLocalPath(repository) && (
<div className="md:col-span-2">
<div className="text-sm font-medium text-muted-foreground">Effective Local Path</div>
<p className="mt-1 text-sm font-mono">{getEffectiveLocalPath(repository)}</p>
</div>
)}
<div>
<div className="text-sm font-medium text-muted-foreground">Created at</div>
<p className="mt-1 text-sm">{new Date(repository.createdAt).toLocaleString()}</p>