Files
api/plugin/builder/utils/rclone-helper.ts
Eli Bosley 5517e7506b feat: add rclone (#1362)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced full RClone remote management with creation, deletion,
listing, and detailed remote info via a multi-step, schema-driven UI.
- Added guided configuration forms supporting advanced and
provider-specific options for RClone remotes.
  - Enabled flash backup initiation through API mutations.
- Added new Vue components for RClone configuration, overview, remote
item cards, and flash backup page.
- Integrated new combobox, stepped layout, control wrapper, label
renderer, and improved form renderers with enhanced validation and error
display.
- Added JSON Forms visibility composable and Unraid settings layout for
consistent UI rendering.

- **Bug Fixes**
- Standardized JSON scalar usage in Docker-related types, replacing
`JSONObject` with `JSON`.

- **Chores**
- Added utility scripts and helpers to manage rclone binary installation
and versioning.
- Updated build scripts and Storybook configuration for CSS handling and
improved developer workflow.
- Refactored ESLint config for modularity and enhanced code quality
enforcement.
- Improved component registration with runtime type checks and error
handling.

- **Documentation**
- Added extensive test coverage for RClone API service, JSON Forms
schema merging, and provider config slice generation.

- **Style**
- Improved UI consistency with new layouts, tooltips on select options,
password visibility toggles, and error handling components.
- Removed deprecated components and consolidated renderer registrations
for JSON Forms.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-05-27 07:52:25 -04:00

64 lines
2.3 KiB
TypeScript

import { join } from "path";
import { existsSync, mkdirSync, createWriteStream, readFileSync } from "fs";
import { writeFile, readFile, unlink } from "fs/promises";
import { get } from "https";
import { $ } from "zx";
import { startingDir } from "./consts";
const RCLONE_VERSION_PATHS = [
join(startingDir, "..", ".rclone-version"),
join(startingDir, ".rclone-version"),
];
const findRcloneVersion = () => {
for (const path of RCLONE_VERSION_PATHS) {
if (existsSync(path)) {
return readFileSync(path, "utf8").trim();
}
}
throw new Error(".rclone-version file not found");
};
const RCLONE_VERSION = findRcloneVersion();
const RCLONE_FILENAME = `rclone-v${RCLONE_VERSION}-linux-amd64.zip`;
const RCLONE_URL = `https://downloads.rclone.org/v${RCLONE_VERSION}/${RCLONE_FILENAME}`;
const RCLONE_DEST = join(startingDir, "source", "dynamix.unraid.net", "usr", "local", "rclone");
const RCLONE_VERSION_FILE = join(RCLONE_DEST, ".rclone-version");
const RCLONE_BIN = join(RCLONE_DEST, "rclone");
async function fetchFile(url: string, dest: string) {
return new Promise((resolve, reject) => {
const file = createWriteStream(dest);
get(url, (response) => {
if (response.statusCode !== 200) {
reject(new Error(`Failed to get '${url}' (${response.statusCode})`));
return;
}
response.pipe(file);
file.on("finish", () => file.close(resolve));
file.on("error", reject);
}).on("error", reject);
});
}
export async function ensureRclone() {
let currentVersion: string | null = null;
if (existsSync(RCLONE_VERSION_FILE)) {
currentVersion = (await readFile(RCLONE_VERSION_FILE, "utf8")).trim();
}
if (currentVersion !== RCLONE_VERSION) {
mkdirSync(RCLONE_DEST, { recursive: true });
if (!existsSync(RCLONE_FILENAME)) {
await fetchFile(RCLONE_URL, RCLONE_FILENAME);
}
await $`unzip -oj ${RCLONE_FILENAME} rclone-v${RCLONE_VERSION}-linux-amd64/rclone -d ${RCLONE_DEST}`;
await $`chmod +x ${RCLONE_BIN}`;
await writeFile(RCLONE_VERSION_FILE, RCLONE_VERSION, "utf8");
// Clean up old rclone archives
const glob = await import("glob");
const files = glob.sync("rclone-v*-linux-amd64.zip", { cwd: startingDir });
for (const file of files) {
if (file !== RCLONE_FILENAME) await unlink(join(startingDir, file));
}
}
}