refactor: define overwrite mode in shared schema

This commit is contained in:
Nicolas Meienberger 2025-11-29 16:49:46 +01:00
parent f4a7f6f737
commit 7be290405f
6 changed files with 32 additions and 19 deletions

View file

@ -23,9 +23,9 @@ import type { Snapshot, Volume } from "~/client/lib/types";
import { toast } from "sonner";
import { listSnapshotFilesOptions, restoreSnapshotMutation } from "~/client/api-client/@tanstack/react-query.gen";
import { useFileBrowser } from "~/client/hooks/use-file-browser";
import { OVERWRITE_MODES, type OverwriteMode } from "~/schemas/restic";
type RestoreLocation = "original" | "custom";
type OverwriteMode = "always" | "if-changed" | "if-newer" | "never";
interface Props {
snapshot: Snapshot;
@ -297,19 +297,21 @@ export const SnapshotFileBrowser = (props: Props) => {
<SelectValue placeholder="Select overwrite behavior" />
</SelectTrigger>
<SelectContent>
<SelectItem value="always">Always overwrite</SelectItem>
<SelectItem value="if-changed">Only if content changed</SelectItem>
<SelectItem value="if-newer">Only if snapshot is newer</SelectItem>
<SelectItem value="never">Never overwrite</SelectItem>
<SelectItem value={OVERWRITE_MODES.always}>Always overwrite</SelectItem>
<SelectItem value={OVERWRITE_MODES.ifChanged}>Only if content changed</SelectItem>
<SelectItem value={OVERWRITE_MODES.ifNewer}>Only if snapshot is newer</SelectItem>
<SelectItem value={OVERWRITE_MODES.never}>Never overwrite</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{overwriteMode === "always" && "Existing files will always be replaced with the snapshot version."}
{overwriteMode === "if-changed" &&
{overwriteMode === OVERWRITE_MODES.always &&
"Existing files will always be replaced with the snapshot version."}
{overwriteMode === OVERWRITE_MODES.ifChanged &&
"Files are only replaced if their content differs from the snapshot."}
{overwriteMode === "if-newer" &&
{overwriteMode === OVERWRITE_MODES.ifNewer &&
"Files are only replaced if the snapshot version has a newer modification time."}
{overwriteMode === "never" && "Existing files will never be replaced, only missing files are restored."}
{overwriteMode === OVERWRITE_MODES.never &&
"Existing files will never be replaced, only missing files are restored."}
</p>
</div>

View file

@ -21,8 +21,7 @@ import type { Repository } from "~/client/lib/types";
import { slugify } from "~/client/lib/utils";
import { updateRepositoryMutation } from "~/client/api-client/@tanstack/react-query.gen";
import type { UpdateRepositoryResponse } from "~/client/api-client/types.gen";
type CompressionMode = "off" | "auto" | "fastest" | "better" | "max";
import type { CompressionMode } from "~/schemas/restic";
type Props = {
repository: Repository;

View file

@ -105,3 +105,12 @@ export const REPOSITORY_STATUS = {
} as const;
export type RepositoryStatus = keyof typeof REPOSITORY_STATUS;
export const OVERWRITE_MODES = {
always: "always",
ifChanged: "if-changed",
ifNewer: "if-newer",
never: "never",
} as const;
export type OverwriteMode = (typeof OVERWRITE_MODES)[keyof typeof OVERWRITE_MODES];

View file

@ -1,6 +1,12 @@
import { type } from "arktype";
import { describeRoute, resolver } from "hono-openapi";
import { COMPRESSION_MODES, REPOSITORY_BACKENDS, REPOSITORY_STATUS, repositoryConfigSchema } from "~/schemas/restic";
import {
COMPRESSION_MODES,
OVERWRITE_MODES,
REPOSITORY_BACKENDS,
REPOSITORY_STATUS,
repositoryConfigSchema,
} from "~/schemas/restic";
export const repositorySchema = type({
id: "string",
@ -269,8 +275,7 @@ export const listSnapshotFilesDto = describeRoute({
/**
* Restore a snapshot
*/
export const overwriteModeSchema = type("'always' | 'if-changed' | 'if-newer' | 'never'");
export type OverwriteMode = typeof overwriteModeSchema.infer;
export const overwriteModeSchema = type.valueOf(OVERWRITE_MODES);
export const restoreSnapshotBody = type({
snapshotId: "string",

View file

@ -8,7 +8,7 @@ import { toMessage } from "../../utils/errors";
import { generateShortId } from "../../utils/id";
import { restic } from "../../utils/restic";
import { cryptoUtils } from "../../utils/crypto";
import type { CompressionMode, RepositoryConfig } from "~/schemas/restic";
import type { CompressionMode, OverwriteMode, RepositoryConfig } from "~/schemas/restic";
const listRepositories = async () => {
const repositories = await db.query.repositoriesTable.findMany({});
@ -207,7 +207,7 @@ const restoreSnapshot = async (
excludeXattr?: string[];
delete?: boolean;
targetPath?: string;
overwrite?: "always" | "if-changed" | "if-newer" | "never";
overwrite?: OverwriteMode;
},
) => {
const repository = await db.query.repositoriesTable.findFirst({

View file

@ -9,7 +9,7 @@ import { logger } from "./logger";
import { cryptoUtils } from "./crypto";
import type { RetentionPolicy } from "../modules/backups/backups.dto";
import { safeSpawn } from "./spawn";
import type { CompressionMode, RepositoryConfig } from "~/schemas/restic";
import type { CompressionMode, RepositoryConfig, OverwriteMode } from "~/schemas/restic";
import { ResticError } from "./errors";
const backupOutputSchema = type({
@ -361,8 +361,6 @@ const restoreOutputSchema = type({
bytes_skipped: "number",
});
type OverwriteMode = "always" | "if-changed" | "if-newer" | "never";
const restore = async (
config: RepositoryConfig,
snapshotId: string,