From 52046c88ccba62e3cded6740ba6cc79edcad523c Mon Sep 17 00:00:00 2001 From: Renan Bernordi Date: Fri, 14 Nov 2025 22:37:27 -0300 Subject: [PATCH 1/2] support cloudflare r2 --- .../components/create-repository-form.tsx | 63 +++++++++++++++++++ app/schemas/restic.ts | 10 +++ .../repositories/repositories.service.ts | 18 ++++++ app/server/utils/restic.ts | 10 +++ 4 files changed, 101 insertions(+) diff --git a/app/client/components/create-repository-form.tsx b/app/client/components/create-repository-form.tsx index 120e0f6..40d4ffc 100644 --- a/app/client/components/create-repository-form.tsx +++ b/app/client/components/create-repository-form.tsx @@ -36,6 +36,7 @@ type Props = { const defaultValuesForType = { local: { backend: "local" as const, compressionMode: "auto" as const }, s3: { backend: "s3" as const, compressionMode: "auto" as const }, + r2: { backend: "r2" as const, compressionMode: "auto" as const }, gcs: { backend: "gcs" as const, compressionMode: "auto" as const }, azure: { backend: "azure" as const, compressionMode: "auto" as const }, rclone: { backend: "rclone" as const, compressionMode: "auto" as const }, @@ -115,6 +116,7 @@ export const CreateRepositoryForm = ({ Local S3 + Cloudflare R2 Google Cloud Storage Azure Blob Storage @@ -222,6 +224,67 @@ export const CreateRepositoryForm = ({ )} + {watchedBackend === "r2" && ( + <> + ( + + Endpoint + + + + R2 endpoint (without https://). Find in R2 dashboard under bucket settings. + + + )} + /> + ( + + Bucket + + + + R2 bucket name for storing backups. + + + )} + /> + ( + + Access Key ID + + + + R2 API token Access Key ID (create in Cloudflare R2 dashboard). + + + )} + /> + ( + + Secret Access Key + + + + R2 API token Secret Access Key (shown once when creating token). + + + )} + /> + + )} + {watchedBackend === "gcs" && ( <> { @@ -19,6 +20,7 @@ const encryptConfig = async (config: RepositoryConfig): Promise { return `${REPOSITORY_BASE}/${config.name}`; case "s3": return `s3:${config.endpoint}/${config.bucket}`; + case "r2": { + const endpoint = config.endpoint.replace(/^https?:\/\//, ''); + return `s3:${endpoint}/${config.bucket}`; + } case "gcs": return `gs:${config.bucket}:/`; case "azure": @@ -98,6 +102,12 @@ const buildEnv = async (config: RepositoryConfig) => { env.AWS_ACCESS_KEY_ID = await cryptoUtils.decrypt(config.accessKeyId); env.AWS_SECRET_ACCESS_KEY = await cryptoUtils.decrypt(config.secretAccessKey); break; + case "r2": + env.AWS_ACCESS_KEY_ID = await cryptoUtils.decrypt(config.accessKeyId); + env.AWS_SECRET_ACCESS_KEY = await cryptoUtils.decrypt(config.secretAccessKey); + env.AWS_REGION = "auto"; + env.AWS_S3_FORCE_PATH_STYLE = "true"; + break; case "gcs": { const decryptedCredentials = await cryptoUtils.decrypt(config.credentialsJson); const credentialsPath = path.join("/tmp", `gcs-credentials-${crypto.randomBytes(8).toString("hex")}.json`); From f517438a8edc7dcd4f4692f2b85405145fdc85a6 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Sat, 15 Nov 2025 09:42:29 +0100 Subject: [PATCH 2/2] refactor(repository): keep the error if repo is already init --- .../modules/repositories/repositories.service.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/server/modules/repositories/repositories.service.ts b/app/server/modules/repositories/repositories.service.ts index 9ebc0f3..f6c3bae 100644 --- a/app/server/modules/repositories/repositories.service.ts +++ b/app/server/modules/repositories/repositories.service.ts @@ -82,21 +82,6 @@ const createRepository = async (name: string, config: RepositoryConfig, compress } const errorMessage = toMessage(error); - - if (errorMessage.includes("already initialized") || errorMessage.includes("config file already exists")) { - logger.info(`Repository already exists on backend, connecting to existing repository: ${slug}`); - - await db - .update(repositoriesTable) - .set({ - status: "healthy", - lastChecked: Date.now(), - lastError: null, - }) - .where(eq(repositoriesTable.id, id)); - - return { repository: created, status: 201 }; - } await db.delete(repositoriesTable).where(eq(repositoriesTable.id, id));