support delete backup file
Some checks are pending
Build / windows (arm64) (push) Waiting to run
Build / windows (ia32) (push) Waiting to run
Build / windows (x64) (push) Waiting to run
Build / linux (arm64) (push) Waiting to run
Build / linux (x64) (push) Waiting to run
Build / macos (arm64) (push) Waiting to run
Build / macos (x64) (push) Waiting to run
Build / updater (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-bin) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron-bin) (push) Blocked by required conditions
Build / aur-git-updater (push) Waiting to run

This commit is contained in:
pompurin404 2024-08-23 14:59:03 +08:00
parent ba484070ae
commit be04031e82
No known key found for this signature in database
7 changed files with 85 additions and 32 deletions

View File

@ -1,3 +1,7 @@
### New Features
- 支持删除 Webdav 备份文件
### Bug Fixes ### Bug Fixes
- 修复拨号网络系统代理问题 - 修复拨号网络系统代理问题

View File

@ -26,6 +26,7 @@
"@mihomo-party/sysproxy": "^2.0.0", "@mihomo-party/sysproxy": "^2.0.0",
"adm-zip": "^0.5.15", "adm-zip": "^0.5.15",
"axios": "^1.7.3", "axios": "^1.7.3",
"dayjs": "^1.11.13",
"webdav": "^5.7.1", "webdav": "^5.7.1",
"ws": "^8.18.0", "ws": "^8.18.0",
"yaml": "^2.5.0" "yaml": "^2.5.0"
@ -38,6 +39,7 @@
"@electron-toolkit/eslint-config-ts": "^2.0.0", "@electron-toolkit/eslint-config-ts": "^2.0.0",
"@electron-toolkit/tsconfig": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1",
"@nextui-org/react": "^2.4.6", "@nextui-org/react": "^2.4.6",
"@types/adm-zip": "^0.5.5",
"@types/node": "^22.1.0", "@types/node": "^22.1.0",
"@types/pubsub-js": "^1.8.6", "@types/pubsub-js": "^1.8.6",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
@ -46,7 +48,6 @@
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
"apexcharts": "^3.52.0", "apexcharts": "^3.52.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"dayjs": "^1.11.12",
"electron": "^31.3.1", "electron": "^31.3.1",
"electron-builder": "^25.0.3", "electron-builder": "^25.0.3",
"electron-vite": "^2.3.0", "electron-vite": "^2.3.0",

View File

@ -23,6 +23,9 @@ importers:
axios: axios:
specifier: ^1.7.3 specifier: ^1.7.3
version: 1.7.4 version: 1.7.4
dayjs:
specifier: ^1.11.13
version: 1.11.13
webdav: webdav:
specifier: ^5.7.1 specifier: ^5.7.1
version: 5.7.1 version: 5.7.1
@ -54,6 +57,9 @@ importers:
'@nextui-org/react': '@nextui-org/react':
specifier: ^2.4.6 specifier: ^2.4.6
version: 2.4.6(@types/react@18.3.3)(framer-motion@11.3.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.10) version: 2.4.6(@types/react@18.3.3)(framer-motion@11.3.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.10)
'@types/adm-zip':
specifier: ^0.5.5
version: 0.5.5
'@types/node': '@types/node':
specifier: ^22.1.0 specifier: ^22.1.0
version: 22.4.0 version: 22.4.0
@ -78,9 +84,6 @@ importers:
autoprefixer: autoprefixer:
specifier: ^10.4.20 specifier: ^10.4.20
version: 10.4.20(postcss@8.4.41) version: 10.4.20(postcss@8.4.41)
dayjs:
specifier: ^1.11.12
version: 1.11.12
electron: electron:
specifier: ^31.3.1 specifier: ^31.3.1
version: 31.4.0 version: 31.4.0
@ -1961,6 +1964,9 @@ packages:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
'@types/adm-zip@0.5.5':
resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==}
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@ -2583,8 +2589,8 @@ packages:
resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dayjs@1.11.12: dayjs@1.11.13:
resolution: {integrity: sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==} resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
debug@4.3.6: debug@4.3.6:
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
@ -7531,6 +7537,10 @@ snapshots:
'@tootallnate/once@2.0.0': {} '@tootallnate/once@2.0.0': {}
'@types/adm-zip@0.5.5':
dependencies:
'@types/node': 22.4.0
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.25.3 '@babel/parser': 7.25.3
@ -8304,7 +8314,7 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
is-data-view: 1.0.1 is-data-view: 1.0.1
dayjs@1.11.12: {} dayjs@1.11.13: {}
debug@4.3.6: debug@4.3.6:
dependencies: dependencies:

View File

@ -1,4 +1,5 @@
import { getAppConfig } from '../config' import { getAppConfig } from '../config'
import dayjs from 'dayjs'
import AdmZip from 'adm-zip' import AdmZip from 'adm-zip'
import { import {
appConfigPath, appConfigPath,
@ -23,7 +24,8 @@ export async function webdavBackup(): Promise<boolean> {
zip.addLocalFile(overrideConfigPath()) zip.addLocalFile(overrideConfigPath())
zip.addLocalFolder(profilesDir(), 'profiles') zip.addLocalFolder(profilesDir(), 'profiles')
zip.addLocalFolder(overrideDir(), 'override') zip.addLocalFolder(overrideDir(), 'override')
const zipFileName = `backup-${new Date().toISOString().replace(/:/g, '-')}.zip` const date = new Date()
const zipFileName = `Backup_${dayjs(date).format('YYYY-MM-DD_HH-mm-ss')}.zip`
const client = createClient(webdavUrl, { const client = createClient(webdavUrl, {
username: webdavUsername, username: webdavUsername,
@ -47,8 +49,8 @@ export async function webdavRestore(filename: string): Promise<void> {
username: webdavUsername, username: webdavUsername,
password: webdavPassword password: webdavPassword
}) })
const zipData = await client.getFileContents(`/mihomo-party/${filename}`) const zipData = await client.getFileContents(`mihomo-party/${filename}`)
const zip = new AdmZip(zipData) const zip = new AdmZip(zipData as Buffer)
zip.extractAllTo(dataDir(), true) zip.extractAllTo(dataDir(), true)
app.relaunch() app.relaunch()
app.quit() app.quit()
@ -70,3 +72,15 @@ export async function listWebdavBackups(): Promise<string[]> {
return files.data.map((file) => file.basename) return files.data.map((file) => file.basename)
} }
} }
export async function webdavDelete(filename: string): Promise<void> {
const webdav = await import('webdav')
const createClient = webdav.createClient
const { webdavUrl = '', webdavUsername = '', webdavPassword = '' } = await getAppConfig()
const client = createClient(webdavUrl, {
username: webdavUsername,
password: webdavPassword
})
await client.deleteFile(`mihomo-party/${filename}`)
}

View File

@ -52,7 +52,7 @@ import { checkUpdate, downloadAndInstallUpdate } from '../resolve/autoUpdater'
import { getFilePath, openUWPTool, readTextFile, setNativeTheme, setupFirewall } from '../sys/misc' import { getFilePath, openUWPTool, readTextFile, setNativeTheme, setupFirewall } from '../sys/misc'
import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory' import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory'
import { isPortable, setPortable } from './dirs' import { isPortable, setPortable } from './dirs'
import { listWebdavBackups, webdavBackup, webdavRestore } from '../resolve/backup' import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore } from '../resolve/backup'
import { getInterfaces } from '../sys/interface' import { getInterfaces } from '../sys/interface'
import { copyEnv } from '../resolve/tray' import { copyEnv } from '../resolve/tray'
import { registerShortcut } from '../resolve/shortcut' import { registerShortcut } from '../resolve/shortcut'
@ -162,6 +162,7 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('webdavBackup', ipcErrorWrapper(webdavBackup)) ipcMain.handle('webdavBackup', ipcErrorWrapper(webdavBackup))
ipcMain.handle('webdavRestore', (_e, filename) => ipcErrorWrapper(webdavRestore)(filename)) ipcMain.handle('webdavRestore', (_e, filename) => ipcErrorWrapper(webdavRestore)(filename))
ipcMain.handle('listWebdavBackups', ipcErrorWrapper(listWebdavBackups)) ipcMain.handle('listWebdavBackups', ipcErrorWrapper(listWebdavBackups))
ipcMain.handle('webdavDelete', (_e, filename) => ipcErrorWrapper(webdavDelete)(filename))
ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) => ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) =>
ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action) ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action)
) )

View File

@ -1,12 +1,14 @@
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react' import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react'
import { webdavRestore } from '@renderer/utils/ipc' import { webdavDelete, webdavRestore } from '@renderer/utils/ipc'
import React, { useState } from 'react' import React, { useState } from 'react'
import { MdDeleteForever } from 'react-icons/md'
interface Props { interface Props {
filenames: string[] filenames: string[]
onClose: () => void onClose: () => void
} }
const WebdavRestoreModal: React.FC<Props> = (props) => { const WebdavRestoreModal: React.FC<Props> = (props) => {
const { filenames, onClose } = props const { filenames: names, onClose } = props
const [filenames, setFilenames] = useState<string[]>(names)
const [restoring, setRestoring] = useState(false) const [restoring, setRestoring] = useState(false)
return ( return (
@ -24,25 +26,42 @@ const WebdavRestoreModal: React.FC<Props> = (props) => {
<div className="flex justify-center"></div> <div className="flex justify-center"></div>
) : ( ) : (
filenames.map((filename) => ( filenames.map((filename) => (
<Button <div className="flex" key={filename}>
size="sm" <Button
fullWidth size="sm"
key={filename} fullWidth
isLoading={restoring} isLoading={restoring}
variant="flat" variant="flat"
onPress={async () => { onPress={async () => {
setRestoring(true) setRestoring(true)
try { try {
await webdavRestore(filename) await webdavRestore(filename)
} catch (e) { } catch (e) {
alert(`恢复失败: ${e}`) alert(`恢复失败: ${e}`)
} finally { } finally {
setRestoring(false) setRestoring(false)
} }
}} }}
> >
{filename} {filename}
</Button> </Button>
<Button
size="sm"
color="warning"
variant="flat"
className="ml-2"
onClick={async () => {
try {
await webdavDelete(filename)
setFilenames(filenames.filter((name) => name !== filename))
} catch (e) {
alert(`删除失败: ${e}`)
}
}}
>
<MdDeleteForever className="text-lg" />
</Button>
</div>
)) ))
)} )}
</ModalBody> </ModalBody>

View File

@ -283,6 +283,10 @@ export async function listWebdavBackups(): Promise<string[]> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('listWebdavBackups')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('listWebdavBackups'))
} }
export async function webdavDelete(filename: string): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavDelete', filename))
}
export async function quitApp(): Promise<void> { export async function quitApp(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp'))
} }