Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"core:window:allow-internal-toggle-maximize",
"autostart:allow-enable",
"autostart:allow-disable",
"autostart:allow-is-enabled"
"autostart:allow-is-enabled",
"dialog:default"
]
}
18 changes: 18 additions & 0 deletions src-tauri/src/cmd/http_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,21 @@ pub async fn update_process(
Err(format!("Failed to update process: {}", response.status()))
}
}

#[tauri::command]
pub async fn delete_process(id: String, _state: State<'_, AppState>) -> Result<bool, String> {
let api_key = get_api_key();
let port = get_server_port();
let client = reqwest::Client::new();
let response = client
.delete(format!("http://127.0.0.1:{port}/api/v1/processes/{id}"))
.header("Authorization", format!("Bearer {api_key}"))
.send()
.await
.map_err(|e| format!("Failed to send request: {e}"))?;
if response.status().is_success() {
Ok(true)
} else {
Err(format!("Failed to delete process: {}", response.status()))
}
}
3 changes: 2 additions & 1 deletion src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use cmd::custom_updater::{
is_auto_check_enabled, restart_app, set_auto_check_enabled,
};
use cmd::http_api::{
get_process_list, restart_process, start_process, stop_process, update_process,
delete_process, get_process_list, restart_process, start_process, stop_process, update_process,
};
use cmd::logs::{clear_logs, get_admin_password, get_logs};
use cmd::openlist_core::{create_openlist_core_process, get_openlist_core_status};
Expand Down Expand Up @@ -117,6 +117,7 @@ pub fn run() {
stop_process,
restart_process,
update_process,
delete_process,
create_openlist_core_process,
get_openlist_core_status,
get_rclone_backend_status,
Expand Down
4 changes: 4 additions & 0 deletions src/api/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export class TauriAPI {
return await invoke('update_process', { id, updateConfig: config })
}

static async deleteProcess(id: string): Promise<boolean> {
return await invoke('delete_process', { id })
}

// OpenList Core management

static async createOpenListCore(autoStart: boolean): Promise<ProcessConfig> {
Expand Down
12 changes: 9 additions & 3 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,10 @@
"usernamePlaceholder": "Username",
"password": "Password",
"passwordPlaceholder": "Password",
"mountPoint": "Mount Point",
"mountPoint": "Mount Path",
"mountPointPlaceholder": "e.g., T: (Windows) or /mnt/remote (Linux)",
"volumeName": "Volume Name",
"volumeNamePlaceholder": "e.g., My Cloud Storage",
"volumeName": "Remote Path",
"volumeNamePlaceholder": "e.g., /",
"autoMount": "Auto-mount on startup",
"advancedSettings": "Advanced Settings",
"extraFlags": "Extra Flags",
Expand All @@ -480,6 +480,7 @@
"messages": {
"fillRequiredFields": "Please fill in all required fields",
"mountPointRequired": "Mount point is required",
"confirmDeleteTitle": "Delete Configuration",
"confirmDelete": "Are you sure you want to delete the remote configuration \"{name}\"?",
"failedToSave": "Failed to save configuration",
"failedToMount": "Failed to mount remote",
Expand All @@ -488,6 +489,11 @@
"failedToRefresh": "Failed to refresh mount status",
"failedToStartService": "Failed to start rclone service",
"failedToStopService": "Failed to stop rclone service"
},
"tip": {
"webdavTitle": "Enable WebDAV Management Required",
"webdavMessage": "Before mounting remotes, please ensure WebDAV management for specific user is enabled in OpenList Core.",
"dismissForever": "Dismiss forever"
}
},
"app": {
Expand Down
10 changes: 8 additions & 2 deletions src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,8 @@
"passwordPlaceholder": "密码",
"mountPoint": "挂载点",
"mountPointPlaceholder": "例如:T: (Windows) 或 /mnt/remote (Linux)",
"volumeName": "卷名",
"volumeNamePlaceholder": "例如:我的云存储",
"volumeName": "远程路径",
"volumeNamePlaceholder": "例如:/",
"autoMount": "开机自动挂载",
"advancedSettings": "高级设置",
"extraFlags": "额外标志",
Expand All @@ -480,6 +480,7 @@
"messages": {
"fillRequiredFields": "请填写所有必填字段",
"mountPointRequired": "挂载点为必填项",
"confirmDeleteTitle": "删除配置",
"confirmDelete": "您确定要删除远程配置 \"{name}\" 吗?",
"failedToSave": "保存配置失败",
"failedToMount": "挂载远程失败",
Expand All @@ -488,6 +489,11 @@
"failedToRefresh": "刷新挂载状态失败",
"failedToStartService": "启动 rclone 服务失败",
"failedToStopService": "停止 rclone 服务失败"
},
"tip": {
"webdavTitle": "需要启用 WebDAV 管理功能",
"webdavMessage": "在挂载远程存储之前,请确保在 OpenList 核心中为用户启用了 WebDAV 管理功能",
"dismissForever": "永久关闭"
}
},
"app": {
Expand Down
140 changes: 99 additions & 41 deletions src/stores/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const useAppStore = defineStore('app', () => {
ssl_enabled: false
},
rclone: {
config: {} // Flexible JSON object for rclone configuration
config: {}
},
app: {
theme: 'light',
Expand Down Expand Up @@ -88,8 +88,8 @@ export const useAppStore = defineStore('app', () => {
throw new Error('Failed to create remote configuration')
}
settings.value.rclone.config[name] = fullConfig
await saveSettings()
await loadRemoteConfigs()
await saveSettings()
return true
} catch (err: any) {
error.value = 'Failed to create remote configuration'
Expand All @@ -104,7 +104,7 @@ export const useAppStore = defineStore('app', () => {
try {
loading.value = true
const fullConfig = {
name,
name: config.name,
type,
url: config.url,
vendor: config.vendor || undefined,
Expand All @@ -121,13 +121,56 @@ export const useAppStore = defineStore('app', () => {
user: fullConfig.user,
pass: fullConfig.pass
}
const result = await TauriAPI.updateRemoteConfig(name, type, updatedConfig)
if (!result) {
throw new Error('Failed to update remote configuration')
if (name !== config.name) {
const result = createRemoteConfig(config.name, type, config)
if (!result) {
throw new Error('Failed to create remote configuration')
}
const deleteResult = await TauriAPI.deleteRemoteConfig(name)
if (!deleteResult) {
throw new Error('Failed to delete old remote configuration')
}
} else {
const result = await TauriAPI.updateRemoteConfig(name, type, updatedConfig)
if (!result) {
throw new Error('Failed to update remote configuration')
}
settings.value.rclone.config[config.name] = fullConfig
}
settings.value.rclone.config[name] = fullConfig
await saveSettings()
await loadRemoteConfigs()
if (name !== config.name && settings.value.rclone.config[name]) {
delete settings.value.rclone.config[name]
}
const oldProcessId = await getRcloneMountProcessId(name)
if (oldProcessId) {
try {
await TauriAPI.stopProcess(oldProcessId)
await TauriAPI.deleteProcess(oldProcessId)

const mountArgs = [
`${fullConfig.name}:${fullConfig.volumeName || ''}`,
fullConfig.mountPoint || '',
...(fullConfig.extraFlags || [])
]
const newProcessConfig: ProcessConfig = {
id: `rclone_mount_${fullConfig.name}_process`,
name: `rclone_mount_${fullConfig.name}_process`,
args: mountArgs,
auto_start: fullConfig.autoMount,
bin_path: 'rclone',
log_file: '',
auto_restart: true,
run_as_admin: false,
created_at: 0,
updated_at: 0
}
await TauriAPI.createRcloneMountRemoteProcess(newProcessConfig)
} catch (err) {
console.warn(`Failed to update mount process for renamed config ${name} -> ${config.name}:`, err)
}
}
await saveSettings()
await loadMountInfos()
return true
} catch (err: any) {
error.value = 'Failed to update remote configuration'
Expand All @@ -141,8 +184,22 @@ export const useAppStore = defineStore('app', () => {
async function deleteRemoteConfig(name: string) {
try {
loading.value = true
const processId = await getRcloneMountProcessId(name)
if (processId) {
try {
await TauriAPI.stopProcess(processId)
await TauriAPI.deleteProcess(processId)
} catch (err) {
console.warn(`Failed to stop/delete mount process for ${name}:`, err)
}
}
await TauriAPI.deleteRemoteConfig(name)
await loadRemoteConfigs()
if (settings.value.rclone.config[name]) {
delete settings.value.rclone.config[name]
await saveSettings()
}
await loadMountInfos()
return true
} catch (err: any) {
error.value = 'Failed to delete remote configuration'
Expand All @@ -168,8 +225,9 @@ export const useAppStore = defineStore('app', () => {
const fullRcloneConfigs = computed(() => {
const result: RcloneFormConfig[] = []
for (const [key, config] of Object.entries(remoteConfigs.value)) {
let newConfig
if (settings.value.rclone.config[key]) {
result.push({
newConfig = {
name: key,
type: 'webdav',
url: config.url,
Expand All @@ -182,20 +240,19 @@ export const useAppStore = defineStore('app', () => {
extraOptions: settings.value.rclone.config[key].extraOptions || {},
autoMount: settings.value.rclone.config[key].autoMount ?? false,
metadata: settings.value.rclone.config[key].metadata || {}
} as RcloneFormConfig)
} as RcloneFormConfig
} else {
const newConfig = {
newConfig = {
...defaultRcloneFormConfig,
name: key,
url: config.url,
vendor: config.vendor,
user: config.user,
pass: config.pass
} as RcloneFormConfig
result.push(newConfig)
settings.value.rclone.config[key] = newConfig
saveSettings().catch(console.error)
}
result.push(newConfig)
settings.value.rclone.config[key] = newConfig
}
return result
})
Expand All @@ -211,7 +268,6 @@ export const useAppStore = defineStore('app', () => {
if (!config) {
throw new Error(`No configuration found for remote: ${name}`)
}

const processId = await getRcloneMountProcessId(name)
console.log(`Mounting remote ${name} with process ID:`, processId)
if (processId) {
Expand All @@ -230,33 +286,34 @@ export const useAppStore = defineStore('app', () => {
console.log(`Remote ${name} is already mounted`)
return
}
} else {
const mountArgs = [
`${config.name}:${config.volumeName || ''}`,
config.mountPoint || '',
...(config.extraFlags || [])
]
const createRemoteConfig: ProcessConfig = {
id: `rclone_mount_${name}_process`,
name: `rclone_mount_${name}_process`,
args: mountArgs,
auto_start: config.autoMount,
bin_path: 'rclone',
log_file: '',
auto_restart: true,
run_as_admin: false,
created_at: 0,
updated_at: 0
}
const createResponse = await TauriAPI.createRcloneMountRemoteProcess(createRemoteConfig)
if (!createResponse || !createResponse.id) {
throw new Error('Failed to create mount process')
}
const startResponse = await TauriAPI.startProcess(createResponse.id)
if (!startResponse) {
throw new Error('Failed to start mount process')
}
await loadMountInfos()
}
const mountArgs = [
`${config.name}:${config.volumeName || ''}`,
config.mountPoint || '',
...(config.extraFlags || [])
]
const createRemoteConfig: ProcessConfig = {
id: `rclone_mount_${name}_process`,
name: `rclone_mount_${name}_process`,
args: mountArgs,
auto_start: config.autoMount,
bin_path: 'rclone',
log_file: '',
auto_restart: true,
run_as_admin: false,
created_at: 0,
updated_at: 0
}
const createResponse = await TauriAPI.createRcloneMountRemoteProcess(createRemoteConfig)
if (!createResponse || !createResponse.id) {
throw new Error('Failed to create mount process')
}
const startResponse = await TauriAPI.startProcess(createResponse.id)
if (!startResponse) {
throw new Error('Failed to start mount process')
}
await loadMountInfos()
} catch (err: any) {
error.value = `Failed to mount remote ${name}: ${formatError(err)}`
console.error('Failed to mount remote:', err)
Expand Down Expand Up @@ -325,6 +382,7 @@ export const useAppStore = defineStore('app', () => {

async function saveSettings() {
try {
console.log('value:', JSON.stringify(settings.value))
await TauriAPI.saveSettings(settings.value)
} catch (err) {
error.value = 'Failed to save settings'
Expand Down
1 change: 0 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export interface AppConfig {
auto_update_enabled?: boolean
}

// Backend structure - this matches MergedSettings in Rust
export interface MergedSettings {
openlist: OpenListCoreConfig
rclone: RcloneConfig
Expand Down
Loading
Loading