From 8f8352090c988ae8e027dbec0e6c4acafd1c48ea Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Fri, 23 May 2025 11:05:32 -0400 Subject: [PATCH] fix: rclone pretry and unnecessary escapes --- api/dev/configs/connect.json | 3 +++ .../jsonforms/rclone-jsonforms-config.ts | 2 +- .../resolvers/rclone/rclone-api.service.ts | 23 ++++++++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/api/dev/configs/connect.json b/api/dev/configs/connect.json index e69de29bb..7ec3d55f8 100644 --- a/api/dev/configs/connect.json +++ b/api/dev/configs/connect.json @@ -0,0 +1,3 @@ +{ + "demo": "hello.unraider" +} \ No newline at end of file diff --git a/api/src/unraid-api/graph/resolvers/rclone/jsonforms/rclone-jsonforms-config.ts b/api/src/unraid-api/graph/resolvers/rclone/jsonforms/rclone-jsonforms-config.ts index 71e32d413..f08fd6c67 100644 --- a/api/src/unraid-api/graph/resolvers/rclone/jsonforms/rclone-jsonforms-config.ts +++ b/api/src/unraid-api/graph/resolvers/rclone/jsonforms/rclone-jsonforms-config.ts @@ -67,7 +67,7 @@ function translateRCloneOptionToJsonSchema({ case 'sizesuffix': // Pattern allows 'off' or digits followed by optional size units (K, M, G, T, P) and optional iB/B // Allows multiple concatenated values like 1G100M - schema.pattern = '^(off|(d+([KMGTPE]i?B?)?)+)$'; + schema.pattern = '^(off|(\\d+([KMGTPE]i?B?)?)+)$'; schema.errorMessage = 'Invalid size format. Examples: "10G", "100M", "1.5GiB", "off".'; break; case 'duration': diff --git a/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts b/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts index 6f0b7518f..46ac5aa3b 100644 --- a/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts +++ b/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts @@ -6,6 +6,7 @@ import { dirname, join } from 'node:path'; import { execa } from 'execa'; import got, { HTTPError } from 'got'; +import pRetry from 'p-retry'; import { RCloneProviderOptionResponse, @@ -122,14 +123,20 @@ export class RCloneApiService implements OnModuleInit, OnModuleDestroy { this.isInitialized = false; }); - // Consider the service initialized shortly after starting the process - // A better approach might involve checking the socket or API readiness - await new Promise((resolve) => setTimeout(resolve, 1000)); // Small delay - // Re-check if socket is running after attempting start - const isRunning = await this.checkRcloneSocketRunning(); - if (!isRunning) { - throw new Error('Rclone socket failed to start or become responsive.'); - } + // Wait for socket to be ready using p-retry with exponential backoff + await pRetry( + async () => { + const isRunning = await this.checkRcloneSocketRunning(); + if (!isRunning) throw new Error('Rclone socket not ready'); + }, + { + retries: 6, // 7 attempts total + minTimeout: 100, + maxTimeout: 5000, + factor: 2, + maxRetryTime: 30000, + } + ); return true; } catch (error: unknown) {