diff --git a/package.json b/package.json index 45412cf..878d463 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,9 @@ "@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/utils": "^3.0.0", "@mihomo-party/sysproxy": "^2.0.0", + "adm-zip": "^0.5.15", "axios": "^1.7.3", + "webdav": "^5.7.1", "ws": "^8.18.0", "yaml": "^2.5.0" }, @@ -40,7 +42,6 @@ "@types/react-dom": "^18.3.0", "@types/ws": "^8.5.12", "@vitejs/plugin-react": "^4.3.1", - "adm-zip": "^0.5.15", "autoprefixer": "^10.4.20", "dayjs": "^1.11.12", "electron": "^31.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eba58d7..d131a0c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,9 +17,15 @@ importers: '@mihomo-party/sysproxy': specifier: ^2.0.0 version: 2.0.0 + adm-zip: + specifier: ^0.5.15 + version: 0.5.15 axios: specifier: ^1.7.3 version: 1.7.3 + webdav: + specifier: ^5.7.1 + version: 5.7.1 ws: specifier: ^8.18.0 version: 8.18.0 @@ -66,9 +72,6 @@ importers: '@vitejs/plugin-react': specifier: ^4.3.1 version: 4.3.1(vite@5.3.5(@types/node@22.1.0)) - adm-zip: - specifier: ^0.5.15 - version: 0.5.15 autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.41) @@ -273,6 +276,9 @@ packages: resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} engines: {node: '>=6.9.0'} + '@buttercup/fetch@0.2.1': + resolution: {integrity: sha512-sCgECOx8wiqY8NN1xN22BqqKzXYIG2AicNLlakOAI4f0WgyLVUbAigMf8CZhBtJxdudTcB1gD5lciqi44jwJvg==} + '@develar/schema-utils@2.6.5': resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} engines: {node: '>= 8.9.0'} @@ -2126,6 +2132,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -2179,6 +2188,9 @@ packages: builder-util@25.0.3: resolution: {integrity: sha512-eH5c1ukdY2xjtFQWQ6jlzEuXuqcuAVc3UQ6V6fdYu9Kg3CkDbCR82Mox42uaJDmee9WXSbP/88cOworFdOHPhw==} + byte-length@1.0.2: + resolution: {integrity: sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -2218,6 +2230,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2359,6 +2374,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2367,6 +2385,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -2529,6 +2551,10 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + entities@5.0.0: + resolution: {integrity: sha512-BeJFvFRJddxobhvEdm5GqHzRV/X+ACeuw0/BuuxsCh1EUZcAIz8+kYmBp/LrQuloy6K1f3a0M7+IhmZ7QnkISA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -2674,12 +2700,20 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -2726,6 +2760,10 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -2907,6 +2945,9 @@ packages: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} + hot-patcher@2.0.1: + resolution: {integrity: sha512-ECg1JFG0YzehicQaogenlcs2qg6WsXQsxtnbr1i696u5tLUjtJdQAh0u2g0Q5YV45f263Ta1GnUJsc8WIfJf4Q==} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -2999,6 +3040,9 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -3184,6 +3228,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + layerr@3.0.0: + resolution: {integrity: sha512-tv754Ki2dXpPVApOrjTyRo4/QegVb9eVFq4mjqp4+NM5NaX7syQvN5BBNfV/ZpAHCEHV24XdUVrBAoka4jt3pA==} + lazy-val@1.0.5: resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} @@ -3286,6 +3333,9 @@ packages: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3442,6 +3492,9 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + nested-property@4.0.0: + resolution: {integrity: sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==} + next-themes@0.3.0: resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==} peerDependencies: @@ -3458,6 +3511,14 @@ packages: node-api-version@0.2.0: resolution: {integrity: sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-gyp@9.4.1: resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} engines: {node: ^12.13 || ^14.13 || >=16} @@ -3576,6 +3637,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-posix@1.0.0: + resolution: {integrity: sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==} + path-scurry@1.11.1: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} @@ -3708,6 +3772,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3844,6 +3911,9 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resedit@1.7.0: resolution: {integrity: sha512-dbsZ0gk5opWPFlKMqvxCrLCuMZUVmsW3yTPT0tT4mYwo5fjQM8c4HMN9ZJt6dRDqDV/78m9SU4rv24PN4NiYaA==} engines: {node: '>=12', npm: '>=6'} @@ -4070,6 +4140,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -4244,6 +4317,13 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-callback-ref@1.3.2: resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} engines: {node: '>=10'} @@ -4354,6 +4434,14 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webdav@5.7.1: + resolution: {integrity: sha512-JVPn3nLxXJfHSRvennHsOrDYjFLkilZ1Qlw8Ff6hpqp6AvkgF7a//aOh5wA4rMp+sLZ1Km0V+iv0LyO1FIwtXg==} + engines: {node: '>=16'} + which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -4590,6 +4678,10 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@buttercup/fetch@0.2.1': + optionalDependencies: + node-fetch: 3.3.2 + '@develar/schema-utils@2.6.5': dependencies: ajv: 6.12.6 @@ -7415,6 +7507,8 @@ snapshots: balanced-match@1.0.2: {} + base-64@1.0.0: {} + base64-js@1.5.1: {} binary-extensions@2.3.0: {} @@ -7512,6 +7606,8 @@ snapshots: transitivePeerDependencies: - supports-color + byte-length@1.0.2: {} + cac@6.7.14: {} cacache@16.1.3: @@ -7574,6 +7670,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + charenc@0.0.2: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -7705,10 +7803,14 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypt@0.0.2: {} + cssesc@3.0.0: {} csstype@3.1.3: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -7923,6 +8025,8 @@ snapshots: dependencies: once: 1.4.0 + entities@5.0.0: {} + env-paths@2.2.1: {} err-code@2.0.3: {} @@ -8188,6 +8292,10 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-xml-parser@4.4.1: + dependencies: + strnum: 1.0.5 + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -8196,6 +8304,11 @@ snapshots: dependencies: pend: 1.2.0 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -8240,6 +8353,10 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fraction.js@4.3.7: {} framer-motion@11.3.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -8449,6 +8566,8 @@ snapshots: dependencies: lru-cache: 6.0.0 + hot-patcher@2.0.1: {} + http-cache-semantics@4.1.1: {} http-proxy-agent@5.0.0: @@ -8553,6 +8672,8 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-buffer@1.1.6: {} + is-callable@1.2.7: {} is-ci@3.0.1: @@ -8716,6 +8837,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + layerr@3.0.0: {} + lazy-val@1.0.5: {} lazystream@1.0.1: @@ -8817,6 +8940,12 @@ snapshots: escape-string-regexp: 4.0.0 optional: true + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + merge2@1.4.1: {} meta-json-schema@1.18.6: {} @@ -8959,6 +9088,8 @@ snapshots: negotiator@0.6.3: {} + nested-property@4.0.0: {} + next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -8975,6 +9106,14 @@ snapshots: dependencies: semver: 7.6.3 + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-gyp@9.4.1: dependencies: env-paths: 2.2.1 @@ -9104,6 +9243,8 @@ snapshots: path-parse@1.0.7: {} + path-posix@1.0.0: {} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 @@ -9206,6 +9347,8 @@ snapshots: punycode@2.3.1: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} quick-lru@5.1.1: {} @@ -9358,6 +9501,8 @@ snapshots: require-directory@2.1.1: {} + requires-port@1.0.0: {} + resedit@1.7.0: dependencies: pe-library: 0.4.0 @@ -9635,6 +9780,8 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@1.0.5: {} + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -9856,6 +10003,13 @@ snapshots: dependencies: punycode: 2.3.1 + url-join@5.0.0: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + use-callback-ref@1.3.2(@types/react@18.3.3)(react@18.3.1): dependencies: react: 18.3.1 @@ -9933,6 +10087,25 @@ snapshots: dependencies: defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} + + webdav@5.7.1: + dependencies: + '@buttercup/fetch': 0.2.1 + base-64: 1.0.0 + byte-length: 1.0.2 + entities: 5.0.0 + fast-xml-parser: 4.4.1 + hot-patcher: 2.0.1 + layerr: 3.0.0 + md5: 2.3.0 + minimatch: 9.0.5 + nested-property: 4.0.0 + node-fetch: 3.3.2 + path-posix: 1.0.0 + url-join: 5.0.0 + url-parse: 1.5.10 + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 diff --git a/src/main/resolve/backup.ts b/src/main/resolve/backup.ts new file mode 100644 index 0000000..ff80873 --- /dev/null +++ b/src/main/resolve/backup.ts @@ -0,0 +1,72 @@ +import { getAppConfig } from '../config' +import AdmZip from 'adm-zip' +import { + appConfigPath, + controledMihomoConfigPath, + dataDir, + overrideConfigPath, + overrideDir, + profileConfigPath, + profilesDir +} from '../utils/dirs' +import { app } from 'electron' + +export async function webdavBackup(): Promise { + const webdav = await import('webdav') + const createClient = webdav.createClient + const { webdavUrl = '', webdavUsername = '', webdavPassword = '' } = await getAppConfig() + const zip = new AdmZip() + + zip.addLocalFile(appConfigPath()) + zip.addLocalFile(controledMihomoConfigPath()) + zip.addLocalFile(profileConfigPath()) + zip.addLocalFile(overrideConfigPath()) + zip.addLocalFolder(profilesDir(), 'profiles') + zip.addLocalFolder(overrideDir(), 'override') + const zipFileName = `backup-${new Date().toISOString().replace(/:/g, '-')}.zip` + + const client = createClient(webdavUrl, { + username: webdavUsername, + password: webdavPassword + }) + try { + await client.createDirectory('mihomo-party') + } catch { + // ignore + } + + return await client.putFileContents(`mihomo-party/${zipFileName}`, zip.toBuffer()) +} + +export async function webdavRestore(filename: string): Promise { + const webdav = await import('webdav') + const createClient = webdav.createClient + const { webdavUrl = '', webdavUsername = '', webdavPassword = '' } = await getAppConfig() + + const client = createClient(webdavUrl, { + username: webdavUsername, + password: webdavPassword + }) + const zipData = await client.getFileContents(`/mihomo-party/${filename}`) + const zip = new AdmZip(zipData) + zip.extractAllTo(dataDir(), true) + app.relaunch() + app.quit() +} + +export async function listWebdavBackups(): Promise { + const webdav = await import('webdav') + const createClient = webdav.createClient + const { webdavUrl = '', webdavUsername = '', webdavPassword = '' } = await getAppConfig() + + const client = createClient(webdavUrl, { + username: webdavUsername, + password: webdavPassword + }) + const files = await client.getDirectoryContents('mihomo-party', { glob: '*.zip' }) + if (Array.isArray(files)) { + return files.map((file) => file.basename) + } else { + return files.data.map((file) => file.basename) + } +} diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index 5499522..06df11f 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -50,6 +50,7 @@ import { checkUpdate } from '../resolve/autoUpdater' import { getFilePath, openUWPTool, readTextFile, setupFirewall } from '../sys/misc' import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory' import { isPortable, setPortable } from './dirs' +import { listWebdavBackups, webdavBackup, webdavRestore } from '../resolve/backup' function ipcErrorWrapper( // eslint-disable-next-line @typescript-eslint/no-explicit-any fn: (...args: any[]) => Promise // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -137,5 +138,8 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('setupFirewall', ipcErrorWrapper(setupFirewall)) ipcMain.handle('setPortable', (_e, portable) => ipcErrorWrapper(setPortable)(portable)) ipcMain.handle('isPortable', isPortable) + ipcMain.handle('webdavBackup', ipcErrorWrapper(webdavBackup)) + ipcMain.handle('webdavRestore', (_e, filename) => ipcErrorWrapper(webdavRestore)(filename)) + ipcMain.handle('listWebdavBackups', ipcErrorWrapper(listWebdavBackups)) ipcMain.handle('quitApp', () => app.quit()) } diff --git a/src/renderer/src/components/settings/webdav-restore-modal.tsx b/src/renderer/src/components/settings/webdav-restore-modal.tsx new file mode 100644 index 0000000..607d3fc --- /dev/null +++ b/src/renderer/src/components/settings/webdav-restore-modal.tsx @@ -0,0 +1,59 @@ +import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react' +import { webdavRestore } from '@renderer/utils/ipc' +import React, { useState } from 'react' +interface Props { + filenames: string[] + onClose: () => void +} +const WebdavRestoreModal: React.FC = (props) => { + const { filenames, onClose } = props + const [restoring, setRestoring] = useState(false) + + return ( + + + 恢复备份 + + {filenames.length === 0 ? ( +
还没有备份
+ ) : ( + filenames.map((filename) => ( + + )) + )} +
+ + + +
+
+ ) +} + +export default WebdavRestoreModal diff --git a/src/renderer/src/pages/settings.tsx b/src/renderer/src/pages/settings.tsx index 9c6b0a0..d2212c1 100644 --- a/src/renderer/src/pages/settings.tsx +++ b/src/renderer/src/pages/settings.tsx @@ -12,7 +12,9 @@ import { patchControledMihomoConfig, isPortable, setPortable, - restartCore + restartCore, + webdavBackup, + listWebdavBackups } from '@renderer/utils/ipc' import { IoLogoGithub } from 'react-icons/io5' import { platform, version } from '@renderer/utils/init' @@ -20,6 +22,7 @@ import useSWR from 'swr' import { Key, useState } from 'react' import debounce from '@renderer/utils/debounce' import { useTheme } from 'next-themes' +import WebdavRestoreModal from '@renderer/components/settings/webdav-restore-modal' const Settings: React.FC = () => { const { setTheme } = useTheme() @@ -37,8 +40,15 @@ const Settings: React.FC = () => { autoCheckUpdate, userAgent, autoCloseConnection = true, - appTheme = 'system' + appTheme = 'system', + webdavUrl, + webdavUsername, + webdavPassword } = appConfig || {} + const [backuping, setBackuping] = useState(false) + const [restoring, setRestoring] = useState(false) + const [filenames, setFilenames] = useState([]) + const [restoreOpen, setRestoreOpen] = useState(false) const [url, setUrl] = useState(delayTestUrl) const setUrlDebounce = debounce((v: string) => { patchAppConfig({ delayTestUrl: v }) @@ -47,7 +57,10 @@ const Settings: React.FC = () => { const setUaDebounce = debounce((v: string) => { patchAppConfig({ userAgent: v }) }, 500) - + const [webdav, setWebdav] = useState({ webdavUrl, webdavUsername, webdavPassword }) + const setWebdavDebounce = debounce(({ webdavUrl, webdavUsername, webdavPassword }) => { + patchAppConfig({ webdavUrl, webdavUsername, webdavPassword }) + }, 500) const onThemeChange = (key: Key, type: 'theme' | 'color'): void => { const [theme, color] = appTheme.split('-') @@ -72,238 +85,323 @@ const Settings: React.FC = () => { } } + const handleBackup = async (): Promise => { + setBackuping(true) + try { + await webdavBackup() + new window.Notification('备份成功', { body: '备份文件已上传至WebDav' }) + } catch (e) { + alert(e) + } finally { + setBackuping(false) + } + } + + const handleRestore = async (): Promise => { + try { + setRestoring(true) + const filenames = await listWebdavBackups() + setRestoring(false) + setFilenames(filenames) + setRestoreOpen(true) + } catch (e) { + alert(`获取备份列表失败: ${e}`) + } + } + return ( - { - window.open('https://github.com/pompurin404/mihomo-party') - }} - > - - - } - > - - - + {restoreOpen && ( + setRestoreOpen(false)} /> + )} + + { - try { - if (v) { - await enableAutoRun() - } else { - await disableAutoRun() - } - } catch (e) { - alert(e) - } finally { - mutateEnable() - } + onPress={() => { + window.open('https://github.com/pompurin404/mihomo-party') }} - /> - - - { - patchAppConfig({ autoCheckUpdate: v }) - }} - /> - - - { - patchAppConfig({ silentStart: v }) - }} - /> - - {platform === 'darwin' && ( - <> - - { - await patchAppConfig({ useDockIcon: v }) - }} - /> - - - { - await patchAppConfig({ showTraffic: v }) - await restartCore() - }} - /> - - - )} - {platform === 'win32' && ( - - + /> - )} - - - { - onThemeChange(key, 'theme') - }} - > - - - - - - - {appTheme !== 'system' && ( - + + { + patchAppConfig({ autoCheckUpdate: v }) + }} + /> + + + { + patchAppConfig({ silentStart: v }) + }} + /> + + {platform === 'darwin' && ( + <> + + { + await patchAppConfig({ useDockIcon: v }) + }} + /> + + + { + await patchAppConfig({ showTraffic: v }) + await restartCore() + }} + /> + + + )} + {platform === 'win32' && ( + + + + )} + { - onThemeChange(key, 'color') + onThemeChange(key, 'theme') }} > - - - + + + + - )} - - - - { - setUa(v) - setUaDebounce(v) - }} - > - - - { - setUrl(v) - setUrlDebounce(v) - }} - > - - - { - patchAppConfig({ delayTestTimeout: parseInt(v) }) - }} - /> - - - { - await patchAppConfig({ controlDns: v }) - await patchControledMihomoConfig({}) - }} - /> - - - { - await patchAppConfig({ controlSniff: v }) - await patchControledMihomoConfig({}) - }} - /> - - - { - patchAppConfig({ autoCloseConnection: v }) - }} - /> - - - - - + + + + + + { + setUa(v) + setUaDebounce(v) + }} + > + + + { + setUrl(v) + setUrlDebounce(v) + }} + > + + + { + patchAppConfig({ delayTestTimeout: parseInt(v) }) + }} + /> + + + { + await patchAppConfig({ controlDns: v }) + await patchControledMihomoConfig({}) + }} + /> + + + { + await patchAppConfig({ controlSniff: v }) + await patchControledMihomoConfig({}) + }} + /> + + + { + patchAppConfig({ autoCloseConnection: v }) + }} + /> + + + + + - - - - - -
v{version}
-
-
-
+ }} + > + 检查更新 + + + + + + +
v{version}
+
+ + + ) } diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 815616e..be2088a 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -253,6 +253,18 @@ export async function isPortable(): Promise { return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('isPortable')) } +export async function webdavBackup(): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavBackup')) +} + +export async function webdavRestore(filename: string): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('webdavRestore', filename)) +} + +export async function listWebdavBackups(): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('listWebdavBackups')) +} + export async function quitApp(): Promise { return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp')) } diff --git a/src/shared/types.d.ts b/src/shared/types.d.ts index 2df526d..4e98d3c 100644 --- a/src/shared/types.d.ts +++ b/src/shared/types.d.ts @@ -218,6 +218,9 @@ interface IAppConfig { controlSniff?: boolean useDockIcon?: boolean showTraffic?: boolean + webdavUrl?: string + webdavUsername?: string + webdavPassword?: string useNameserverPolicy: boolean nameserverPolicy: { [key: string]: string | string[] } }