diff --git a/lib/rclone/api.ts b/lib/rclone/api.ts index 26cdb45..4d3c438 100644 --- a/lib/rclone/api.ts +++ b/lib/rclone/api.ts @@ -1,5 +1,3 @@ -import { appLocalDataDir } from '@tauri-apps/api/path' -import { exists } from '@tauri-apps/plugin-fs' import { fetch } from '@tauri-apps/plugin-http' import { platform } from '@tauri-apps/plugin-os' import { useStore } from '../store' @@ -711,60 +709,3 @@ export async function getMountFlags() { return filteredFlags } - -export async function getConfigPath({ id, validate = true }: { id: string; validate?: boolean }) { - console.log('[getConfigPath]', id, validate) - - const appLocalDataDirPath = await appLocalDataDir() - console.log('[getConfigPath] appLocalDataDirPath', appLocalDataDirPath) - - const slashSymbol = platform() === 'windows' ? '\\' : '/' - - let configPath = - appLocalDataDirPath + - slashSymbol + - 'configs' + - slashSymbol + - id + - slashSymbol + - 'rclone.conf' - - if (id == 'default') { - const defaultPaths = await getDefaultPaths() - - if (typeof defaultPaths?.config === 'undefined') { - console.error('[getConfigPath] failed to fetch config path') - throw new Error('Failed to fetch config path') - } - - configPath = defaultPaths.config - } - - if (validate) { - const configExists = await exists(configPath) - if (!configExists) { - console.error('[getConfigPath] config file does not exist') - throw new Error('Config file does not exist') - } - } - - return configPath -} - -export async function getDefaultPaths() { - console.log('[getDefaultPaths]') - - const r = await fetch('http://localhost:5572/config/paths', { - method: 'POST', - }) - - if (!r.ok) { - throw new Error('Failed to make request to config/paths') - } - - const defaultPaths = (await r.json()) as { cache: string; config: string; temp: string } - - console.log('[getDefaultPaths] json', JSON.stringify(defaultPaths, null, 2)) - - return defaultPaths -} diff --git a/lib/rclone/common.ts b/lib/rclone/common.ts new file mode 100644 index 0000000..123e9a7 --- /dev/null +++ b/lib/rclone/common.ts @@ -0,0 +1,153 @@ +import { appLocalDataDir } from '@tauri-apps/api/path' +import { exists } from '@tauri-apps/plugin-fs' +import { fetch } from '@tauri-apps/plugin-http' +import { platform } from '@tauri-apps/plugin-os' +import { Command } from '@tauri-apps/plugin-shell' + +export async function getDefaultPaths() { + console.log('[getDefaultPaths]') + + const r = await fetch('http://localhost:5572/config/paths', { + method: 'POST', + }) + + if (!r.ok) { + console.error('[getDefaultPaths] failed to make request to config/paths') + throw new Error('Failed to make request to config/paths') + } + + const defaultPaths = (await r.json()) as { cache: string; config: string; temp: string } + + console.log('[getDefaultPaths] json', JSON.stringify(defaultPaths, null, 2)) + + return defaultPaths +} + +export async function getDefaultPath(type: 'system' | 'internal') { + console.log('[getDefaultPath]', type) + + let instance = null + if (type === 'system') { + console.log('[getDefaultPath] running system rclone') + instance = Command.create('rclone-system', [ + 'rcd', + '--rc-no-auth', + '--rc-serve', + // '-rc-addr', + // ':5572', + ]) + } + if (type === 'internal') { + console.log('[getDefaultPath] running internal rclone') + instance = Command.create('rclone-internal', [ + 'rcd', + '--rc-no-auth', + '--rc-serve', + // '-rc-addr', + // ':5572', + ]) + } + + if (!instance) { + console.error('[getDefaultPath] failed to create rclone instance') + throw new Error('Failed to create rclone instance, please try again later.') + } + + const output = await instance.spawn() + + console.log('[getDefaultPath] spawned rclone') + + await new Promise((resolve) => setTimeout(resolve, 200)) + + try { + const defaultPaths = await getDefaultPaths() + + if (typeof defaultPaths?.config === 'undefined') { + throw new Error('Failed to fetch config path') + } + + return defaultPaths.config + } catch (error) { + console.error('[getDefaultPath] error', error) + if (error instanceof Error) { + throw error + } + throw new Error('Failed to get default path, please try again later.') + } finally { + await output.kill() + } +} + +export async function getConfigPath({ id, validate = true }: { id: string; validate?: boolean }) { + console.log('[getConfigPath]', id, validate) + + const appLocalDataDirPath = await appLocalDataDir() + console.log('[getConfigPath] appLocalDataDirPath', appLocalDataDirPath) + + const slashSymbol = platform() === 'windows' ? '\\' : '/' + + let configPath = + appLocalDataDirPath + + slashSymbol + + 'configs' + + slashSymbol + + id + + slashSymbol + + 'rclone.conf' + + console.log('[getConfigPath] configPath', configPath) + + if (id == 'default') { + const system = await isSystemRcloneInstalled() + const defaultPath = await getDefaultPath(system ? 'system' : 'internal') + + configPath = defaultPath + } + + if (validate) { + const configExists = await exists(configPath) + if (!configExists) { + console.error('[getConfigPath] config file does not exist') + throw new Error('Config file does not exist') + } + } + + return configPath +} + +/** + * Checks if rclone is installed and accessible from the system PATH + * @returns {Promise} True if rclone is installed and working + */ +export async function isSystemRcloneInstalled() { + console.log('[isSystemRcloneInstalled]') + + try { + const output = await Command.create('rclone-system').execute() + return ( + output.stdout.includes('Available commands') || + output.stderr.includes('Available commands') + ) + } catch { + return false + } +} + +/** + * Checks if rclone is downloaded by the application in the app's local data directory + * @returns {Promise} True if downloaded rclone is present and working + */ +export async function isInternalRcloneInstalled() { + console.log('[isInternalRcloneInstalled]') + + try { + const output = await Command.create('rclone-internal').execute() + // console.log('[isInternalRcloneInstalled] output', output) + return ( + output.stdout.includes('Available commands') || + output.stderr.includes('Available commands') + ) + } catch { + return false + } +} diff --git a/lib/rclone/init.ts b/lib/rclone/init.ts index f49e9b5..21af013 100644 --- a/lib/rclone/init.ts +++ b/lib/rclone/init.ts @@ -9,7 +9,12 @@ import { platform } from '@tauri-apps/plugin-os' import { exit } from '@tauri-apps/plugin-process' import { Command } from '@tauri-apps/plugin-shell' import { usePersistedStore } from '../store' -import { getConfigPath, getDefaultPaths } from './api' +import { + getConfigPath, + getDefaultPath, + isInternalRcloneInstalled, + isSystemRcloneInstalled, +} from './common' export async function initRclone(args: string[]) { console.log('[initRclone]') @@ -139,98 +144,6 @@ export async function initRclone(args: string[]) { throw new Error('Failed to initialize rclone, please try again later.') } -async function getDefaultPath(type: 'system' | 'internal') { - console.log('[getDefaultPath]', type) - - let instance = null - if (type === 'system') { - console.log('[getDefaultPath] running system rclone') - instance = Command.create('rclone-system', [ - 'rcd', - '--rc-no-auth', - '--rc-serve', - // '-rc-addr', - // ':5572', - ]) - } - if (type === 'internal') { - console.log('[getDefaultPath] running internal rclone') - instance = Command.create('rclone-internal', [ - 'rcd', - '--rc-no-auth', - '--rc-serve', - // '-rc-addr', - // ':5572', - ]) - } - - if (!instance) { - console.error('[getDefaultPath] failed to create rclone instance') - throw new Error('Failed to create rclone instance, please try again later.') - } - - const output = await instance.spawn() - - console.log('[getDefaultPath] spawned rclone') - - await new Promise((resolve) => setTimeout(resolve, 200)) - - try { - const defaultPaths = await getDefaultPaths() - - if (typeof defaultPaths?.config === 'undefined') { - throw new Error('Failed to fetch config path') - } - - return defaultPaths.config - } catch (error) { - console.error('getDefaultPath error', error) - if (error instanceof Error) { - throw error - } - throw new Error('Failed to get default path, please try again later.') - } finally { - await output.kill() - } -} - -/** - * Checks if rclone is installed and accessible from the system PATH - * @returns {Promise} True if rclone is installed and working - */ -export async function isSystemRcloneInstalled() { - console.log('[isSystemRcloneInstalled]') - - try { - const output = await Command.create('rclone-system').execute() - return ( - output.stdout.includes('Available commands') || - output.stderr.includes('Available commands') - ) - } catch (_) { - return false - } -} - -/** - * Checks if rclone is downloaded by the application in the app's local data directory - * @returns {Promise} True if downloaded rclone is present and working - */ -export async function isInternalRcloneInstalled() { - console.log('[isInternalRcloneInstalled]') - - try { - const output = await Command.create('rclone-internal').execute() - // console.log('[isInternalRcloneInstalled] output', output) - return ( - output.stdout.includes('Available commands') || - output.stderr.includes('Available commands') - ) - } catch (_) { - return false - } -} - /** * Downloads and provisions the latest version of rclone for the current platform * @throws {Error} If architecture detection fails or installation is unsuccessful diff --git a/src/components/ConfigCreateDrawer.tsx b/src/components/ConfigCreateDrawer.tsx index 0da6c32..a3593d0 100644 --- a/src/components/ConfigCreateDrawer.tsx +++ b/src/components/ConfigCreateDrawer.tsx @@ -13,7 +13,7 @@ import { mkdir, readTextFile, writeTextFile } from '@tauri-apps/plugin-fs' import { platform } from '@tauri-apps/plugin-os' import { UploadIcon } from 'lucide-react' import { useCallback, useMemo, useState } from 'react' -import { getConfigPath } from '../../lib/rclone/api' +import { getConfigPath } from '../../lib/rclone/common' import { usePersistedStore } from '../../lib/store' import type { ConfigFile } from '../../types/config' diff --git a/src/components/ConfigEditDrawer.tsx b/src/components/ConfigEditDrawer.tsx index 32c1e5b..f2ecf6c 100644 --- a/src/components/ConfigEditDrawer.tsx +++ b/src/components/ConfigEditDrawer.tsx @@ -11,7 +11,7 @@ import { Button } from '@heroui/react' import { message } from '@tauri-apps/plugin-dialog' import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs' import { useCallback, useEffect, useMemo, useState } from 'react' -import { getConfigPath } from '../../lib/rclone/api' +import { getConfigPath } from '../../lib/rclone/common' import { usePersistedStore } from '../../lib/store' export default function ConfigEditDrawer({ diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 095c03c..3e583c2 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -32,7 +32,8 @@ import { } from 'lucide-react' import { useCallback, useEffect, useState } from 'react' import { revokeLicense, validateLicense } from '../../lib/license' -import { deleteRemote, getVersion as getCliVersion, getConfigPath } from '../../lib/rclone/api' +import { deleteRemote, getVersion as getCliVersion } from '../../lib/rclone/api' +import { getConfigPath } from '../../lib/rclone/common' import { usePersistedStore, useStore } from '../../lib/store' import { triggerTrayRebuild } from '../../lib/tray' import ConfigCreateDrawer from '../components/ConfigCreateDrawer'