Skip to content

rclone-ui/rclone-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Rclone SDK

Full OpenAPI-based client for the Rclone RC API

npm version npm downloads crates.io crates.io downloads License: MIT

Built on top of rclone-openapi (by yours truly) Β· Works with Vanilla Fetch Β· React Query Β· SWR Β· Rust

πŸ¦€ Rust

cargo add rclone-sdk
[dependencies]
rclone-sdk = "1.72"
tokio = { version = "1", features = ["full"] }
use rclone_sdk::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new("http://localhost:5572");

    // Get rclone version info
    let version = client.core_version(None, None).await?;
    let v = version.into_inner();
    println!("Rclone {} on {}/{}", v.version, v.os, v.arch);

    // List all configured remotes
    let remotes = client.config_listremotes(None, None).await?;
    println!("Remotes: {:?}", remotes.into_inner().remotes);

    // Get storage info for a remote
    let about = client.operations_about(None, None, "gdrive:").await?;
    let info = about.into_inner();
    println!("Storage: {} / {} bytes used", info.used, info.total);

    Ok(())
}

πŸ“¦ JavaScript / TypeScript

npm install rclone-sdk

Vanilla

import createRCDClient from 'rclone-sdk'

const rcd = createRCDClient({ baseUrl: 'http://localhost:5572' })

// List all configured remotes
const { data: remotes } = await rcd.POST('/config/listremotes')
console.log(remotes?.remotes) // ['gdrive', 'dropbox', 's3']

// List files in a remote
const { data: files } = await rcd.POST('/operations/list', {
    body: { fs: 'gdrive:', remote: 'Documents' }
})
console.log(files?.list)

// Get storage info for a remote
const { data: about } = await rcd.POST('/operations/about', {
    body: { fs: 'gdrive:' }
})
console.log(`Used: ${about?.used} / ${about?.total}`)

Tanstack/React Query

import createRCDQueryClient from 'rclone-sdk/query'

const rq = createRCDQueryClient({ baseUrl: 'http://localhost:5572' })

function RemotesList() {
    const { data, isLoading, error } = rq.useQuery('post', '/config/listremotes')

    if (isLoading) return <div>Loading...</div>
    if (error) return <div>Error: {error.message}</div>

    return (
        <ul>
            {data?.remotes?.map(remote => (
                <li key={remote}>{remote}</li>
            ))}
        </ul>
    )
}

function StorageInfo({ remote }: { remote: string }) {
    const { data } = rq.useQuery('post', '/operations/about', {
        body: { fs: `${remote}:` }
    })

    return <span>{data?.used} / {data?.total} bytes</span>
}

SWR

import createRCDSWR from 'rclone-sdk/swr'

const swr = createRCDSWR({ baseUrl: 'http://localhost:5572' })

function RemotesList() {
    const { data, error, isLoading } = swr.useQuery('post', '/config/listremotes')

    if (isLoading) return <div>Loading...</div>
    if (error) return <div>Error: {error.message}</div>

    return (
        <ul>
            {data?.remotes?.map(remote => (
                <li key={remote}>{remote}</li>
            ))}
        </ul>
    )
}

function FileList({ remote, path }: { remote: string; path: string }) {
    const { data } = swr.useQuery('post', '/operations/list', {
        body: { fs: `${remote}:`, remote: path }
    })

    return (
        <ul>
            {data?.list?.map(item => (
                <li key={item.Path}>
                    {item.IsDir ? 'πŸ“' : 'πŸ“„'} {item.Name}
                </li>
            ))}
        </ul>
    )
}

Tips

Even though the client supports all HTTP methods, rclone expects everything as a POST request.

If you want to wrap the client to only send POST requests and throw errors automatically, here's a quick snippet (adjust to taste):

import createRCDClient, {
    type OpenApiMethodResponse,
    type OpenApiClient,
    type OpenApiClientPathsWithMethod,
    type OpenApiMaybeOptionalInit,
    type OpenApiRequiredKeysOf,
    type RCDClient,
} from 'rclone-sdk'

type ClientPaths<T> = T extends OpenApiClient<infer P, any> ? P : never
type Paths = ClientPaths<RCDClient>
type InitParam<Init> = OpenApiRequiredKeysOf<Init> extends never
    ? [(Init & { [key: string]: unknown })?]
    : [Init & { [key: string]: unknown }]

export default async function rclone<
    Path extends OpenApiClientPathsWithMethod<RCDClient, 'post'>,
    Init extends OpenApiMaybeOptionalInit<Paths[Path], 'post'> = OpenApiMaybeOptionalInit<
        Paths[Path],
        'post'
    >,
>(
    path: Path,
    ...init: InitParam<Init>
): Promise<OpenApiMethodResponse<RCDClient, 'post', Path, Init>> {
    const client = createRCDClient({ baseUrl: 'http://localhost:5572' })

    const result = await client.POST(
        path,
        ...(init as InitParam<OpenApiMaybeOptionalInit<Paths[Path], 'post'>>)
    )

    if (result?.error) {
        const message =
            typeof result.error === 'string' ? result.error : JSON.stringify(result.error)

        throw new Error(message)
    }

    const data = result.data as { error?: unknown } | undefined
    if (data?.error) {
        const message = typeof data.error === 'string' ? data.error : JSON.stringify(data.error)

        throw new Error(message)
    }

    if (!result.response.ok) {
        throw new Error(`${result.response.status} ${result.response.statusText}`)
    }

    return result.data as OpenApiMethodResponse<typeof client, 'post', Path, Init>
}

Now you'll be able to

  • call rclone('config/listremotes') directly
  • get the data as the return value (correctly typed, and without having to de-construct the resulting object)
  • have it throw on error with a detailed error message (instead of checking the error field manually)

Contributing

Contributions = welcome! Just make sure to check if the PR isn't a better fit for the rclone-openapi repo.

Made with ☁️ for the rclone community

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published