Skip to content

Commit feb50d9

Browse files
committed
feat(linux): add rclone availability check and installation tip
- Add check_rclone_available command in Rust backend - Add rcloneAvailable state and checkRcloneAvailable method in rclone store - Add TauriAPI.rclone.backend.isAvailable() API call - Add rclone installation tip UI for Linux users when rclone is not found - Add i18n texts for rclone tip in en.json and zh.json - Add CSS styles for rclone-tip component
1 parent 9d69508 commit feb50d9

File tree

9 files changed

+149
-9
lines changed

9 files changed

+149
-9
lines changed

src-tauri/src/cmd/rclone_core.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ use crate::utils::path::{get_app_logs_dir, get_rclone_binary_path, get_rclone_co
1010
// admin:admin base64 encoded
1111
pub const RCLONE_AUTH: &str = "Basic YWRtaW46YWRtaW4=";
1212

13+
#[tauri::command]
14+
pub async fn check_rclone_available() -> Result<bool, String> {
15+
get_rclone_binary_path().map(|_| true).or(Ok(false))
16+
}
17+
1318
#[tauri::command]
1419
pub async fn create_and_start_rclone_backend(
1520
state: State<'_, AppState>,

src-tauri/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ use cmd::os_operate::{
2727
open_url_in_browser, select_directory, update_tool_version,
2828
};
2929
use cmd::rclone_core::{
30-
create_and_start_rclone_backend, create_rclone_backend_process, get_rclone_backend_status,
30+
check_rclone_available, create_and_start_rclone_backend, create_rclone_backend_process,
31+
get_rclone_backend_status,
3132
};
3233
use cmd::rclone_mount::{
3334
check_mount_status, create_rclone_mount_remote_process, get_mount_info_list,
@@ -126,6 +127,7 @@ pub fn run() {
126127
create_openlist_core_process,
127128
get_openlist_core_status,
128129
get_rclone_backend_status,
130+
check_rclone_available,
129131
create_rclone_backend_process,
130132
create_and_start_rclone_backend,
131133
rclone_list_config,

src-tauri/src/utils/path.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,12 @@ pub fn get_rclone_binary_path() -> Result<PathBuf, String> {
124124
#[cfg(not(target_os = "windows"))]
125125
{
126126
use std::process::Command;
127-
if let Ok(output) = Command::new("which").arg("rclone").output() {
128-
if output.status.success() {
129-
let path_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
130-
if !path_str.is_empty() {
131-
return Ok(PathBuf::from(path_str));
132-
}
127+
if let Ok(output) = Command::new("which").arg("rclone").output()
128+
&& output.status.success()
129+
{
130+
let path_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
131+
if !path_str.is_empty() {
132+
return Ok(PathBuf::from(path_str));
133133
}
134134
}
135135
}

src/api/tauri.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export class TauriAPI {
3636
backend: {
3737
create: (): Promise<boolean> => call('create_rclone_backend_process'),
3838
createAndStart: (): Promise<ProcessConfig> => call('create_and_start_rclone_backend'),
39-
isRunning: (): Promise<boolean> => call('get_rclone_backend_status')
39+
isRunning: (): Promise<boolean> => call('get_rclone_backend_status'),
40+
isAvailable: (): Promise<boolean> => call('check_rclone_available')
4041
},
4142
remotes: {
4243
list: (): Promise<string[]> => call('rclone_list_remotes'),

src/i18n/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@
468468
"webdavMessage": "Before mounting remotes, please ensure WebDAV management for specific user is enabled in OpenList Core.",
469469
"winfspTitle": "WinFSP Installation Required",
470470
"winfspMessage": "On Windows, you need to install WinFSP first to use mount functionality. Please download and install it from GitHub: https://github.com/winfsp/winfsp/releases",
471+
"rcloneTitle": "Rclone Installation Required",
472+
"rcloneMessage": "On Linux, rclone is not bundled with the application. Please install it using your package manager (e.g., sudo apt install rclone) or from https://rclone.org/install/",
471473
"dismissForever": "Dismiss forever"
472474
}
473475
},

src/i18n/locales/zh.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@
468468
"webdavMessage": "在挂载远程存储之前,请确保在 OpenList 核心中为用户启用了 WebDAV 管理功能",
469469
"winfspTitle": "需要安装 WinFSP",
470470
"winfspMessage": "在 Windows 系统上,您需要先安装 WinFSP 才能使用挂载功能。请从 GitHub 下载并安装:https://github.com/winfsp/winfsp/releases",
471+
"rcloneTitle": "需要安装 Rclone",
472+
"rcloneMessage": "在 Linux 系统上,rclone 不随应用程序捆绑。请使用包管理器安装(例如:sudo apt install rclone)或从 https://rclone.org/install/ 下载安装",
471473
"dismissForever": "永久关闭"
472474
}
473475
},

src/stores/rclone.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const useRcloneStore = defineStore('rclone', () => {
77
const loading = ref(false)
88
const error = ref<string | undefined>()
99
const serviceRunning = ref(false)
10+
const rcloneAvailable = ref(true)
1011

1112
const setError = (msg?: string) => (error.value = msg)
1213

@@ -65,16 +66,27 @@ export const useRcloneStore = defineStore('rclone', () => {
6566
return running
6667
}
6768

68-
const init = () => console.log('Initializing Rclone store...')
69+
const checkRcloneAvailable = async () => {
70+
const available = await TauriAPI.rclone.backend.isAvailable().catch(() => false)
71+
rcloneAvailable.value = available
72+
return available
73+
}
74+
75+
const init = () => {
76+
console.log('Initializing Rclone store...')
77+
checkRcloneAvailable()
78+
}
6979

7080
return {
7181
loading,
7282
error,
7383
serviceRunning,
84+
rcloneAvailable,
7485
clearError,
7586
startRcloneBackend,
7687
stopRcloneBackend,
7788
checkRcloneBackendStatus,
89+
checkRcloneAvailable,
7890
init
7991
}
8092
})

src/views/MountView.vue

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,23 @@ const dismissWinfspTip = () => {
465465
localStorage.setItem('winfsp_tip_dismissed', 'true')
466466
}
467467
468+
const isLinux = computed(() => {
469+
return typeof OS_PLATFORM !== 'undefined' && OS_PLATFORM === 'linux'
470+
})
471+
const showRcloneTip = ref(false)
472+
473+
const dismissRcloneTip = () => {
474+
showRcloneTip.value = false
475+
localStorage.setItem('rclone_tip_dismissed', 'true')
476+
}
477+
468478
const shouldShowWebdavTip = computed(() => {
469479
if (isWindows.value) {
470480
return !showWinfspTip.value && showWebdavTip.value
471481
}
482+
if (isLinux.value && showRcloneTip.value) {
483+
return false
484+
}
472485
return showWebdavTip.value
473486
})
474487
@@ -482,6 +495,12 @@ onMounted(async () => {
482495
rcloneStore.checkRcloneBackendStatus()
483496
}, 15 * 1000)
484497
rcloneStore.init()
498+
499+
// Check rclone availability on Linux
500+
if (isLinux.value && !localStorage.getItem('rclone_tip_dismissed')) {
501+
const available = await rcloneStore.checkRcloneAvailable()
502+
showRcloneTip.value = !available
503+
}
485504
})
486505
487506
onUnmounted(() => {
@@ -580,6 +599,21 @@ onUnmounted(() => {
580599
</div>
581600
</div>
582601

602+
<div v-if="showRcloneTip" class="rclone-tip">
603+
<div class="tip-content">
604+
<div class="tip-icon">
605+
<HardDrive class="icon" />
606+
</div>
607+
<div class="tip-message">
608+
<h4 class="tip-title">{{ t('mount.tip.rcloneTitle') }}</h4>
609+
<p class="tip-description">{{ t('mount.tip.rcloneMessage') }}</p>
610+
</div>
611+
<button class="tip-close" :title="t('mount.tip.dismissForever')" @click="dismissRcloneTip">
612+
<X class="close-icon" />
613+
</button>
614+
</div>
615+
</div>
616+
583617
<!-- Controls Section -->
584618
<div class="controls-section">
585619
<div class="search-container">

src/views/css/MountView.css

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,88 @@
15701570
color: #60a5fa;
15711571
}
15721572

1573+
/* Rclone tip - orange theme for Linux */
1574+
.rclone-tip {
1575+
position: relative;
1576+
z-index: 1;
1577+
margin: 0 28px 12px;
1578+
background: linear-gradient(135deg, #ffedd5 0%, #fed7aa 100%);
1579+
border: 1px solid #f97316;
1580+
border-radius: 8px;
1581+
box-shadow: 0 1px 4px rgba(249, 115, 22, 0.1);
1582+
overflow: hidden;
1583+
}
1584+
1585+
.rclone-tip .tip-icon {
1586+
background: rgba(249, 115, 22, 0.1);
1587+
}
1588+
1589+
.rclone-tip .tip-icon .icon {
1590+
color: #c2410c;
1591+
}
1592+
1593+
.rclone-tip .tip-title {
1594+
color: #9a3412;
1595+
}
1596+
1597+
.rclone-tip .tip-description {
1598+
color: #c2410c;
1599+
}
1600+
1601+
.rclone-tip .tip-close {
1602+
background: rgba(249, 115, 22, 0.1);
1603+
}
1604+
1605+
.rclone-tip .tip-close:hover {
1606+
background: rgba(249, 115, 22, 0.2);
1607+
}
1608+
1609+
.rclone-tip .tip-close .close-icon {
1610+
color: #c2410c;
1611+
}
1612+
1613+
:root.dark .rclone-tip,
1614+
:root.auto.dark .rclone-tip {
1615+
background: linear-gradient(135deg, #431407 0%, #7c2d12 100%);
1616+
border-color: #f97316;
1617+
box-shadow: 0 1px 4px rgba(249, 115, 22, 0.1);
1618+
}
1619+
1620+
:root.dark .rclone-tip .tip-icon,
1621+
:root.auto.dark .rclone-tip .tip-icon {
1622+
background: rgba(249, 115, 22, 0.1);
1623+
}
1624+
1625+
:root.dark .rclone-tip .tip-icon .icon,
1626+
:root.auto.dark .rclone-tip .tip-icon .icon {
1627+
color: #fb923c;
1628+
}
1629+
1630+
:root.dark .rclone-tip .tip-title,
1631+
:root.auto.dark .rclone-tip .tip-title {
1632+
color: #fdba74;
1633+
}
1634+
1635+
:root.dark .rclone-tip .tip-description,
1636+
:root.auto.dark .rclone-tip .tip-description {
1637+
color: #fb923c;
1638+
}
1639+
1640+
:root.dark .rclone-tip .tip-close,
1641+
:root.auto.dark .rclone-tip .tip-close {
1642+
background: rgba(249, 115, 22, 0.1);
1643+
}
1644+
1645+
:root.dark .rclone-tip .tip-close:hover,
1646+
:root.auto.dark .rclone-tip .tip-close:hover {
1647+
background: rgba(249, 115, 22, 0.2);
1648+
}
1649+
1650+
:root.dark .rclone-tip .tip-close .close-icon,
1651+
:root.auto.dark .rclone-tip .tip-close .close-icon {
1652+
color: #fb923c;
1653+
}
1654+
15731655
@media (max-width: 1024px) {
15741656
.header-content {
15751657
grid-template-columns: 1fr;

0 commit comments

Comments
 (0)