mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Sat Jan 3 19:40:33 CET 2026
This commit is contained in:
@@ -1231,3 +1231,4 @@ Update On Tue Dec 30 19:43:26 CET 2025
|
||||
Update On Wed Dec 31 19:39:51 CET 2025
|
||||
Update On Thu Jan 1 19:42:21 CET 2026
|
||||
Update On Fri Jan 2 19:40:09 CET 2026
|
||||
Update On Sat Jan 3 19:40:25 CET 2026
|
||||
|
||||
@@ -53,7 +53,7 @@ func main() {
|
||||
df := func() {}
|
||||
app := cli.NewApp()
|
||||
app.Name = "Brook"
|
||||
app.Version = "20250808"
|
||||
app.Version = "20260101"
|
||||
app.Usage = "A cross-platform programmable network tool"
|
||||
app.Authors = []*cli.Author{
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.19.18",
|
||||
"mihomo_alpha": "alpha-1f8bee9",
|
||||
"mihomo_alpha": "alpha-4d76703",
|
||||
"clash_rs": "v0.9.3",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.9.3-alpha+sha.626fbd4"
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2025-12-31T22:21:37.371Z"
|
||||
"updated_at": "2026-01-02T22:21:48.399Z"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
||||
|
||||
## [2.53.1](https://github.com/filebrowser/filebrowser/compare/v2.53.0...v2.53.1) (2026-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* download path encoding file paths ([#5655](https://github.com/filebrowser/filebrowser/issues/5655)) ([ffa893e](https://github.com/filebrowser/filebrowser/commit/ffa893e9ac387a49dba5917a41df7c3b7ce120fc))
|
||||
* request a password to change sensitive user data ([#5629](https://github.com/filebrowser/filebrowser/issues/5629)) ([b8151a0](https://github.com/filebrowser/filebrowser/commit/b8151a038a1ea55afae8073b439b74e364cac12f))
|
||||
|
||||
## [2.53.0](https://github.com/filebrowser/filebrowser/compare/v2.52.0...v2.53.0) (2025-12-29)
|
||||
|
||||
|
||||
|
||||
@@ -6,22 +6,23 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEmptyKey = errors.New("empty key")
|
||||
ErrExist = errors.New("the resource already exists")
|
||||
ErrNotExist = errors.New("the resource does not exist")
|
||||
ErrEmptyPassword = errors.New("password is empty")
|
||||
ErrEasyPassword = errors.New("password is too easy")
|
||||
ErrEmptyUsername = errors.New("username is empty")
|
||||
ErrEmptyRequest = errors.New("empty request")
|
||||
ErrScopeIsRelative = errors.New("scope is a relative path")
|
||||
ErrInvalidDataType = errors.New("invalid data type")
|
||||
ErrIsDirectory = errors.New("file is directory")
|
||||
ErrInvalidOption = errors.New("invalid option")
|
||||
ErrInvalidAuthMethod = errors.New("invalid auth method")
|
||||
ErrPermissionDenied = errors.New("permission denied")
|
||||
ErrInvalidRequestParams = errors.New("invalid request params")
|
||||
ErrSourceIsParent = errors.New("source is parent")
|
||||
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
|
||||
ErrEmptyKey = errors.New("empty key")
|
||||
ErrExist = errors.New("the resource already exists")
|
||||
ErrNotExist = errors.New("the resource does not exist")
|
||||
ErrEmptyPassword = errors.New("password is empty")
|
||||
ErrEasyPassword = errors.New("password is too easy")
|
||||
ErrEmptyUsername = errors.New("username is empty")
|
||||
ErrEmptyRequest = errors.New("empty request")
|
||||
ErrScopeIsRelative = errors.New("scope is a relative path")
|
||||
ErrInvalidDataType = errors.New("invalid data type")
|
||||
ErrIsDirectory = errors.New("file is directory")
|
||||
ErrInvalidOption = errors.New("invalid option")
|
||||
ErrInvalidAuthMethod = errors.New("invalid auth method")
|
||||
ErrPermissionDenied = errors.New("permission denied")
|
||||
ErrInvalidRequestParams = errors.New("invalid request params")
|
||||
ErrSourceIsParent = errors.New("source is parent")
|
||||
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
|
||||
ErrCurrentPasswordIncorrect = errors.New("the current password is incorrect")
|
||||
)
|
||||
|
||||
type ErrShortPassword struct {
|
||||
|
||||
@@ -71,5 +71,5 @@
|
||||
"vite-plugin-compression2": "^2.3.1",
|
||||
"vue-tsc": "^3.1.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6"
|
||||
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
||||
}
|
||||
|
||||
Generated
+102
-91
@@ -82,7 +82,7 @@ importers:
|
||||
version: 4.5.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(@vueuse/integrations@14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3)))(focus-trap@7.6.2)(vue@3.5.26(typescript@5.9.3))
|
||||
vue-i18n:
|
||||
specifier: ^11.1.10
|
||||
version: 11.2.7(vue@3.5.26(typescript@5.9.3))
|
||||
version: 11.2.8(vue@3.5.26(typescript@5.9.3))
|
||||
vue-lazyload:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
@@ -98,7 +98,7 @@ importers:
|
||||
devDependencies:
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: ^11.0.1
|
||||
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
|
||||
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
|
||||
'@tsconfig/node24':
|
||||
specifier: ^24.0.2
|
||||
version: 24.0.3
|
||||
@@ -110,7 +110,7 @@ importers:
|
||||
version: 24.10.4
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^8.37.0
|
||||
version: 8.50.1(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
|
||||
version: 8.51.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@vitejs/plugin-legacy':
|
||||
specifier: ^7.2.1
|
||||
version: 7.2.1(terser@5.44.1)(vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2))
|
||||
@@ -983,6 +983,12 @@ packages:
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
|
||||
'@eslint-community/eslint-utils@4.9.1':
|
||||
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
|
||||
'@eslint-community/regexpp@4.12.2':
|
||||
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
@@ -1043,20 +1049,20 @@ packages:
|
||||
vue-i18n:
|
||||
optional: true
|
||||
|
||||
'@intlify/core-base@11.2.7':
|
||||
resolution: {integrity: sha512-+Ra9I/LAzXDnmv/IrTO03WMCiLya7pHRmGJvNl9fKwx/W4REJ0xaMk2PxCRqnxcBsX443amEMdebQ3R1geiuIw==}
|
||||
'@intlify/core-base@11.2.8':
|
||||
resolution: {integrity: sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@11.2.7':
|
||||
resolution: {integrity: sha512-TFamC+GzJAotAFwUNvbtRVBgvuSn2nCwKNresmPUHv3IIVMmXJt7QQJj/DORI1h8hs46ZF6L0Fs2xBohSOE4iQ==}
|
||||
'@intlify/message-compiler@11.2.8':
|
||||
resolution: {integrity: sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@11.2.2':
|
||||
resolution: {integrity: sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@11.2.7':
|
||||
resolution: {integrity: sha512-uvlkvc/0uQ4FDlHQZccpUnmcOwNcaI3i+69ck2YJ+GqM35AoVbuS63b+YfirV4G0SZh64Ij2UMcFRMmB4nr95w==}
|
||||
'@intlify/shared@11.2.8':
|
||||
resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.3':
|
||||
@@ -1283,11 +1289,11 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.50.1':
|
||||
resolution: {integrity: sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==}
|
||||
'@typescript-eslint/eslint-plugin@8.51.0':
|
||||
resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^8.50.1
|
||||
'@typescript-eslint/parser': ^8.51.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
@@ -1310,8 +1316,8 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/project-service@8.50.1':
|
||||
resolution: {integrity: sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==}
|
||||
'@typescript-eslint/project-service@8.51.0':
|
||||
resolution: {integrity: sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
@@ -1324,8 +1330,8 @@ packages:
|
||||
resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/scope-manager@8.50.1':
|
||||
resolution: {integrity: sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==}
|
||||
'@typescript-eslint/scope-manager@8.51.0':
|
||||
resolution: {integrity: sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.37.0':
|
||||
@@ -1340,8 +1346,8 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.50.1':
|
||||
resolution: {integrity: sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==}
|
||||
'@typescript-eslint/tsconfig-utils@8.51.0':
|
||||
resolution: {integrity: sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
@@ -1353,8 +1359,8 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/type-utils@8.50.1':
|
||||
resolution: {integrity: sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==}
|
||||
'@typescript-eslint/type-utils@8.51.0':
|
||||
resolution: {integrity: sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -1368,8 +1374,8 @@ packages:
|
||||
resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/types@8.50.1':
|
||||
resolution: {integrity: sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==}
|
||||
'@typescript-eslint/types@8.51.0':
|
||||
resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.37.0':
|
||||
@@ -1384,8 +1390,8 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.50.1':
|
||||
resolution: {integrity: sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==}
|
||||
'@typescript-eslint/typescript-estree@8.51.0':
|
||||
resolution: {integrity: sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
@@ -1397,8 +1403,8 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/utils@8.50.1':
|
||||
resolution: {integrity: sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==}
|
||||
'@typescript-eslint/utils@8.51.0':
|
||||
resolution: {integrity: sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -1412,8 +1418,8 @@ packages:
|
||||
resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.50.1':
|
||||
resolution: {integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==}
|
||||
'@typescript-eslint/visitor-keys@8.51.0':
|
||||
resolution: {integrity: sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@videojs/http-streaming@3.17.2':
|
||||
@@ -2490,8 +2496,8 @@ packages:
|
||||
systemjs@6.15.1:
|
||||
resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==}
|
||||
|
||||
tabbable@6.3.0:
|
||||
resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==}
|
||||
tabbable@6.4.0:
|
||||
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
|
||||
|
||||
tar-mini@0.2.0:
|
||||
resolution: {integrity: sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==}
|
||||
@@ -2509,8 +2515,8 @@ packages:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
ts-api-utils@2.3.0:
|
||||
resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==}
|
||||
ts-api-utils@2.4.0:
|
||||
resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
|
||||
engines: {node: '>=18.12'}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4'
|
||||
@@ -2669,8 +2675,8 @@ packages:
|
||||
focus-trap: '>=7.2.0'
|
||||
vue: '>=3.2.0'
|
||||
|
||||
vue-i18n@11.2.7:
|
||||
resolution: {integrity: sha512-LPv8bAY5OA0UvFEXl4vBQOBqJzRrlExy92tWgRuwW7tbykHf7CH71G2Y4TM2OwGcIS4+hyqKHS2EVBqaYwPY9Q==}
|
||||
vue-i18n@11.2.8:
|
||||
resolution: {integrity: sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==}
|
||||
engines: {node: '>= 16'}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
@@ -3558,6 +3564,11 @@ snapshots:
|
||||
eslint: 9.39.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)':
|
||||
dependencies:
|
||||
eslint: 9.39.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.2': {}
|
||||
|
||||
'@eslint/config-array@0.21.1':
|
||||
@@ -3610,9 +3621,9 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.3': {}
|
||||
|
||||
'@intlify/bundle-utils@11.0.3(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))':
|
||||
'@intlify/bundle-utils@11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 11.2.7
|
||||
'@intlify/message-compiler': 11.2.8
|
||||
'@intlify/shared': 11.2.2
|
||||
acorn: 8.15.0
|
||||
esbuild: 0.25.12
|
||||
@@ -3622,28 +3633,28 @@ snapshots:
|
||||
source-map-js: 1.2.1
|
||||
yaml-eslint-parser: 1.3.2
|
||||
optionalDependencies:
|
||||
vue-i18n: 11.2.7(vue@3.5.26(typescript@5.9.3))
|
||||
vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3))
|
||||
|
||||
'@intlify/core-base@11.2.7':
|
||||
'@intlify/core-base@11.2.8':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 11.2.7
|
||||
'@intlify/shared': 11.2.7
|
||||
'@intlify/message-compiler': 11.2.8
|
||||
'@intlify/shared': 11.2.8
|
||||
|
||||
'@intlify/message-compiler@11.2.7':
|
||||
'@intlify/message-compiler@11.2.8':
|
||||
dependencies:
|
||||
'@intlify/shared': 11.2.7
|
||||
'@intlify/shared': 11.2.8
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@intlify/shared@11.2.2': {}
|
||||
|
||||
'@intlify/shared@11.2.7': {}
|
||||
'@intlify/shared@11.2.8': {}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
|
||||
'@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
|
||||
'@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))
|
||||
'@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))
|
||||
'@intlify/shared': 11.2.2
|
||||
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
|
||||
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.54.0)
|
||||
'@typescript-eslint/scope-manager': 8.49.0
|
||||
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
|
||||
@@ -3654,7 +3665,7 @@ snapshots:
|
||||
unplugin: 2.3.11
|
||||
vue: 3.5.26(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
vue-i18n: 11.2.7(vue@3.5.26(typescript@5.9.3))
|
||||
vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/compiler-dom'
|
||||
- eslint
|
||||
@@ -3662,14 +3673,14 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
|
||||
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
optionalDependencies:
|
||||
'@intlify/shared': 11.2.2
|
||||
'@vue/compiler-dom': 3.5.26
|
||||
vue: 3.5.26(typescript@5.9.3)
|
||||
vue-i18n: 11.2.7(vue@3.5.26(typescript@5.9.3))
|
||||
vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3))
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
@@ -3822,23 +3833,23 @@ snapshots:
|
||||
graphemer: 1.4.0
|
||||
ignore: 7.0.5
|
||||
natural-compare: 1.4.0
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.50.1(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.51.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.50.1
|
||||
'@typescript-eslint/type-utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.50.1
|
||||
'@typescript-eslint/scope-manager': 8.51.0
|
||||
'@typescript-eslint/type-utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.51.0
|
||||
eslint: 9.39.2
|
||||
ignore: 7.0.5
|
||||
natural-compare: 1.4.0
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3873,10 +3884,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/project-service@8.50.1(typescript@5.9.3)':
|
||||
'@typescript-eslint/project-service@8.51.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.50.1
|
||||
'@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.51.0
|
||||
debug: 4.4.3
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
@@ -3892,10 +3903,10 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.49.0
|
||||
'@typescript-eslint/visitor-keys': 8.49.0
|
||||
|
||||
'@typescript-eslint/scope-manager@8.50.1':
|
||||
'@typescript-eslint/scope-manager@8.51.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.50.1
|
||||
'@typescript-eslint/visitor-keys': 8.50.1
|
||||
'@typescript-eslint/types': 8.51.0
|
||||
'@typescript-eslint/visitor-keys': 8.51.0
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
@@ -3905,7 +3916,7 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.50.1(typescript@5.9.3)':
|
||||
'@typescript-eslint/tsconfig-utils@8.51.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
@@ -3916,19 +3927,19 @@ snapshots:
|
||||
'@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.2
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/type-utils@8.50.1(eslint@9.39.2)(typescript@5.9.3)':
|
||||
'@typescript-eslint/type-utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.50.1
|
||||
'@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.51.0
|
||||
'@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.2
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3937,7 +3948,7 @@ snapshots:
|
||||
|
||||
'@typescript-eslint/types@8.49.0': {}
|
||||
|
||||
'@typescript-eslint/types@8.50.1': {}
|
||||
'@typescript-eslint/types@8.51.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.37.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
@@ -3950,7 +3961,7 @@ snapshots:
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.7.3
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3965,22 +3976,22 @@ snapshots:
|
||||
minimatch: 9.0.5
|
||||
semver: 7.7.3
|
||||
tinyglobby: 0.2.15
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.50.1(typescript@5.9.3)':
|
||||
'@typescript-eslint/typescript-estree@8.51.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/project-service': 8.50.1(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.50.1
|
||||
'@typescript-eslint/visitor-keys': 8.50.1
|
||||
'@typescript-eslint/project-service': 8.51.0(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.51.0
|
||||
'@typescript-eslint/visitor-keys': 8.51.0
|
||||
debug: 4.4.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.7.3
|
||||
tinyglobby: 0.2.15
|
||||
ts-api-utils: 2.3.0(typescript@5.9.3)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3996,12 +4007,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.50.1(eslint@9.39.2)(typescript@5.9.3)':
|
||||
'@typescript-eslint/utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
|
||||
'@typescript-eslint/scope-manager': 8.50.1
|
||||
'@typescript-eslint/types': 8.50.1
|
||||
'@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3)
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
|
||||
'@typescript-eslint/scope-manager': 8.51.0
|
||||
'@typescript-eslint/types': 8.51.0
|
||||
'@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3)
|
||||
eslint: 9.39.2
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
@@ -4017,9 +4028,9 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.49.0
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.50.1':
|
||||
'@typescript-eslint/visitor-keys@8.51.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.50.1
|
||||
'@typescript-eslint/types': 8.51.0
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
'@videojs/http-streaming@3.17.2(video.js@8.23.4)':
|
||||
@@ -4670,7 +4681,7 @@ snapshots:
|
||||
|
||||
focus-trap@7.6.2:
|
||||
dependencies:
|
||||
tabbable: 6.3.0
|
||||
tabbable: 6.4.0
|
||||
|
||||
fraction.js@5.3.4: {}
|
||||
|
||||
@@ -5138,7 +5149,7 @@ snapshots:
|
||||
|
||||
systemjs@6.15.1: {}
|
||||
|
||||
tabbable@6.3.0: {}
|
||||
tabbable@6.4.0: {}
|
||||
|
||||
tar-mini@0.2.0: {}
|
||||
|
||||
@@ -5158,7 +5169,7 @@ snapshots:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
ts-api-utils@2.3.0(typescript@5.9.3):
|
||||
ts-api-utils@2.4.0(typescript@5.9.3):
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
@@ -5313,10 +5324,10 @@ snapshots:
|
||||
focus-trap: 7.6.2
|
||||
vue: 3.5.26(typescript@5.9.3)
|
||||
|
||||
vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)):
|
||||
vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 11.2.7
|
||||
'@intlify/shared': 11.2.7
|
||||
'@intlify/core-base': 11.2.8
|
||||
'@intlify/shared': 11.2.8
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.26(typescript@5.9.3)
|
||||
|
||||
|
||||
@@ -41,12 +41,12 @@ export function download(
|
||||
let url = `${baseURL}/api/public/dl/${hash}`;
|
||||
|
||||
if (files.length === 1) {
|
||||
url += encodeURIComponent(files[0]) + "?";
|
||||
url += files[0] + "?";
|
||||
} else {
|
||||
let arg = "";
|
||||
|
||||
for (const file of files) {
|
||||
arg += encodeURIComponent(file) + ",";
|
||||
arg += file + ",";
|
||||
}
|
||||
|
||||
arg = arg.substring(0, arg.length - 1);
|
||||
|
||||
@@ -8,12 +8,13 @@ export async function get(id: number) {
|
||||
return fetchJSON<IUser>(`/api/users/${id}`, {});
|
||||
}
|
||||
|
||||
export async function create(user: IUser) {
|
||||
export async function create(user: IUser, currentPassword: string) {
|
||||
const res = await fetchURL(`/api/users`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
what: "user",
|
||||
which: [],
|
||||
current_password: currentPassword,
|
||||
data: user,
|
||||
}),
|
||||
});
|
||||
@@ -25,12 +26,17 @@ export async function create(user: IUser) {
|
||||
throw new StatusError(await res.text(), res.status);
|
||||
}
|
||||
|
||||
export async function update(user: Partial<IUser>, which = ["all"]) {
|
||||
export async function update(
|
||||
user: Partial<IUser>,
|
||||
which = ["all"],
|
||||
currentPassword: string | null = null
|
||||
) {
|
||||
await fetchURL(`/api/users/${user.id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
what: "user",
|
||||
which: which,
|
||||
...(currentPassword != null ? { current_password: currentPassword } : {}),
|
||||
data: user,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -258,7 +258,8 @@
|
||||
"userManagement": "User Management",
|
||||
"userUpdated": "User updated!",
|
||||
"username": "Username",
|
||||
"users": "Users"
|
||||
"users": "Users",
|
||||
"currentPassword": "Your Current Password"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Help",
|
||||
|
||||
@@ -5,6 +5,7 @@ interface ISettings {
|
||||
minimumPasswordLength: number;
|
||||
userHomeBasePath: string;
|
||||
defaults: SettingsDefaults;
|
||||
authMethod: string;
|
||||
rules: any[];
|
||||
branding: SettingsBranding;
|
||||
tus: SettingsTus;
|
||||
|
||||
@@ -69,6 +69,15 @@
|
||||
v-model="passwordConf"
|
||||
name="passwordConf"
|
||||
/>
|
||||
<input
|
||||
v-if="isCurrentPasswordRequired"
|
||||
:class="passwordClass"
|
||||
type="password"
|
||||
:placeholder="t('settings.currentPassword')"
|
||||
v-model="currentPassword"
|
||||
name="current_password"
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
@@ -87,7 +96,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { users as api } from "@/api";
|
||||
import { users as api, settings } from "@/api";
|
||||
import AceEditorTheme from "@/components/settings/AceEditorTheme.vue";
|
||||
import Languages from "@/components/settings/Languages.vue";
|
||||
import { computed, inject, onMounted, ref } from "vue";
|
||||
@@ -102,6 +111,8 @@ const $showError = inject<IToastError>("$showError")!;
|
||||
|
||||
const password = ref<string>("");
|
||||
const passwordConf = ref<string>("");
|
||||
const currentPassword = ref<string>("");
|
||||
const isCurrentPasswordRequired = ref<boolean>(false);
|
||||
const hideDotfiles = ref<boolean>(false);
|
||||
const singleClick = ref<boolean>(false);
|
||||
const dateFormat = ref<boolean>(false);
|
||||
@@ -131,6 +142,9 @@ onMounted(async () => {
|
||||
dateFormat.value = authStore.user.dateFormat;
|
||||
aceEditorTheme.value = authStore.user.aceEditorTheme;
|
||||
layoutStore.loading = false;
|
||||
const { authMethod } = await settings.get();
|
||||
isCurrentPasswordRequired.value = authMethod == "json";
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -140,6 +154,7 @@ const updatePassword = async (event: Event) => {
|
||||
if (
|
||||
password.value !== passwordConf.value ||
|
||||
password.value === "" ||
|
||||
currentPassword.value === "" ||
|
||||
authStore.user === null
|
||||
) {
|
||||
return;
|
||||
@@ -151,7 +166,7 @@ const updatePassword = async (event: Event) => {
|
||||
id: authStore.user.id,
|
||||
password: password.value,
|
||||
};
|
||||
await api.update(data, ["password"]);
|
||||
await api.update(data, ["password"], currentPassword.value);
|
||||
authStore.updateUser(data);
|
||||
$showSuccess(t("settings.passwordUpdated"));
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -15,6 +15,19 @@
|
||||
:isDefault="false"
|
||||
:isNew="isNew"
|
||||
/>
|
||||
|
||||
<p v-if="isCurrentPasswordRequired">
|
||||
<label for="currentPassword">{{
|
||||
t("settings.currentPassword")
|
||||
}}</label>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="password"
|
||||
v-model="currentPassword"
|
||||
id="currentPassword"
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
@@ -63,6 +76,8 @@ const error = ref<StatusError>();
|
||||
const originalUser = ref<IUser>();
|
||||
const user = ref<IUser>();
|
||||
const createUserDir = ref<boolean>(false);
|
||||
const currentPassword = ref<string>("");
|
||||
const isCurrentPasswordRequired = ref<boolean>(false);
|
||||
|
||||
const $showError = inject<IToastError>("$showError")!;
|
||||
const $showSuccess = inject<IToastSuccess>("$showSuccess")!;
|
||||
@@ -90,7 +105,12 @@ const fetchData = async () => {
|
||||
|
||||
try {
|
||||
if (isNew.value) {
|
||||
const { defaults, createUserDir: _createUserDir } = await settings.get();
|
||||
const {
|
||||
authMethod,
|
||||
defaults,
|
||||
createUserDir: _createUserDir,
|
||||
} = await settings.get();
|
||||
isCurrentPasswordRequired.value = authMethod == "json";
|
||||
createUserDir.value = _createUserDir;
|
||||
user.value = {
|
||||
...defaults,
|
||||
@@ -101,6 +121,8 @@ const fetchData = async () => {
|
||||
id: 0,
|
||||
};
|
||||
} else {
|
||||
const { authMethod } = await settings.get();
|
||||
isCurrentPasswordRequired.value = authMethod == "json";
|
||||
const id = Array.isArray(route.params.id)
|
||||
? route.params.id.join("")
|
||||
: route.params.id;
|
||||
@@ -151,11 +173,11 @@ const save = async (event: Event) => {
|
||||
...user.value,
|
||||
};
|
||||
|
||||
const loc = await api.create(newUser);
|
||||
const loc = await api.create(newUser, currentPassword.value);
|
||||
router.push({ path: loc || "/settings/users" });
|
||||
$showSuccess(t("settings.userCreated"));
|
||||
} else {
|
||||
await api.update(user.value);
|
||||
await api.update(user.value, ["all"], currentPassword.value);
|
||||
|
||||
if (user.value.id === authStore.user?.id) {
|
||||
authStore.updateUser(user.value);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ require (
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/samber/lo v1.52.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.11
|
||||
github.com/shirou/gopsutil/v4 v4.25.12
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
|
||||
+2
-2
@@ -204,8 +204,8 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type modifyRequest struct {
|
||||
What string `json:"what"` // Answer to: what data type?
|
||||
Which []string `json:"which"` // Answer to: which fields?
|
||||
What string `json:"what"` // Answer to: what data type?
|
||||
Which []string `json:"which"` // Answer to: which fields?
|
||||
CurrentPassword string `json:"current_password"` // Answer to: user logged password
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
|
||||
@@ -15,6 +15,7 @@ type settingsData struct {
|
||||
MinimumPasswordLength uint `json:"minimumPasswordLength"`
|
||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||
Defaults settings.UserDefaults `json:"defaults"`
|
||||
AuthMethod settings.AuthMethod `json:"authMethod"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
Branding settings.Branding `json:"branding"`
|
||||
Tus settings.Tus `json:"tus"`
|
||||
@@ -30,6 +31,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
|
||||
MinimumPasswordLength: d.settings.MinimumPasswordLength,
|
||||
UserHomeBasePath: d.settings.UserHomeBasePath,
|
||||
Defaults: d.settings.Defaults,
|
||||
AuthMethod: d.settings.AuthMethod,
|
||||
Rules: d.settings.Rules,
|
||||
Branding: d.settings.Branding,
|
||||
Tus: d.settings.Tus,
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/filebrowser/filebrowser/v2/auth"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
)
|
||||
@@ -117,6 +118,12 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
if d.settings.AuthMethod == auth.MethodJSONAuth {
|
||||
if !users.CheckPwd(req.CurrentPassword, d.user.Password) {
|
||||
return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect
|
||||
}
|
||||
}
|
||||
|
||||
if len(req.Which) != 0 {
|
||||
return http.StatusBadRequest, nil
|
||||
}
|
||||
@@ -153,6 +160,27 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
if d.settings.AuthMethod == auth.MethodJSONAuth {
|
||||
var sensibleFields = map[string]struct{}{
|
||||
"all": {},
|
||||
"username": {},
|
||||
"password": {},
|
||||
"scope": {},
|
||||
"lockPassword": {},
|
||||
"commands": {},
|
||||
"perm": {},
|
||||
}
|
||||
|
||||
for _, field := range req.Which {
|
||||
if _, ok := sensibleFields[field]; ok {
|
||||
if !users.CheckPwd(req.CurrentPassword, d.user.Password) {
|
||||
return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if req.Data.ID != d.raw.(uint) {
|
||||
return http.StatusBadRequest, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=linux-atm
|
||||
PKG_VERSION:=2.5.2
|
||||
PKG_RELEASE:=7
|
||||
PKG_RELEASE:=8
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=@SF/$(PKG_NAME)
|
||||
@@ -21,6 +21,7 @@ PKG_BUILD_PARALLEL:=1
|
||||
PKG_LICENSE:=GPL-2.0+
|
||||
PKG_CPE_ID:=cpe:/a:linux-atm:linux-atm
|
||||
PKG_FIXUP:=autoreconf
|
||||
PKG_FLAGS:=nonshared
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
@@ -98,6 +99,8 @@ endef
|
||||
|
||||
$(foreach t,$(ATM_DEBUG_TOOLS),$(eval $(call GenAtmPlugin,atm-$(t),$(t))))
|
||||
|
||||
TARGET_CFLAGS += -I$(LINUX_DIR)/user_headers/include
|
||||
|
||||
define Build/Configure
|
||||
$(call Build/Configure/Default)
|
||||
# prevent autoheader invocation
|
||||
|
||||
+91
-42
@@ -1,3 +1,45 @@
|
||||
--- a/src/mpoad/mpcd.8
|
||||
+++ b/src/mpoad/mpcd.8
|
||||
@@ -28,7 +28,7 @@ mpcd \- ATM MPOA (Multi\-Protocol Over A
|
||||
.B ]]
|
||||
.SH DESCRIPTION
|
||||
MPOA client
|
||||
-.SM(MPC) is responsible for creating and receiving
|
||||
+.SM (MPC) is responsible for creating and receiving
|
||||
internetwork layer shortcuts. Using these shortcuts MPCs forward
|
||||
unicast internetwork layer packets effectively over ATM without need
|
||||
for routing protocols.
|
||||
@@ -43,7 +43,7 @@ accepts shortcuts and packets arriving o
|
||||
shortcuts is done with the help of
|
||||
.SM MPOA
|
||||
server
|
||||
-.SM(MPS).
|
||||
+.SM (MPS).
|
||||
.PP
|
||||
Just as the Linux
|
||||
.SM LAN
|
||||
--- a/src/led/zeppelin.8
|
||||
+++ b/src/led/zeppelin.8
|
||||
@@ -99,7 +99,7 @@ Ring and ATM parts of the ELAN, so using
|
||||
recommended. Token Ring support has received less testing than its
|
||||
Ethernet counterpart.
|
||||
.SH FILES
|
||||
-.IP \fI/var/run/lec[interface number].pid\fP
|
||||
+.IP \fI/var/run/lec[interface\ number].pid\fP
|
||||
The file containing the process id of zeppelin.
|
||||
.SH BUGS
|
||||
John Bonham died 1980 and Led Zeppelin broke.
|
||||
--- a/src/sigd/atmsigd.conf.4
|
||||
+++ b/src/sigd/atmsigd.conf.4
|
||||
@@ -125,7 +125,7 @@ a comment. The `#' character cannot be e
|
||||
.P
|
||||
If an option is specified in \fBatmsigd.conf\fP and on the command
|
||||
line, the command line has priority.
|
||||
-.COMPATIBILITY
|
||||
+.SH COMPATIBILITY
|
||||
Certain options used by past versions of \fBatmsigd\fP but no longer documented
|
||||
on the man page are still recognized and supported, but they also yield a
|
||||
warning message. Future versions of \fBatmsigd\fP will not recognize those
|
||||
--- a/src/arpd/io.c
|
||||
+++ b/src/arpd/io.c
|
||||
@@ -277,7 +277,8 @@ static void accept_new(void)
|
||||
@@ -226,45 +268,52 @@
|
||||
|
||||
if (trans) {
|
||||
/* set send socket buffer if we are transmitting */
|
||||
--- a/src/mpoad/mpcd.8
|
||||
+++ b/src/mpoad/mpcd.8
|
||||
@@ -28,7 +28,7 @@ mpcd \- ATM MPOA (Multi\-Protocol Over A
|
||||
.B ]]
|
||||
.SH DESCRIPTION
|
||||
MPOA client
|
||||
-.SM(MPC) is responsible for creating and receiving
|
||||
+.SM (MPC) is responsible for creating and receiving
|
||||
internetwork layer shortcuts. Using these shortcuts MPCs forward
|
||||
unicast internetwork layer packets effectively over ATM without need
|
||||
for routing protocols.
|
||||
@@ -43,7 +43,7 @@ accepts shortcuts and packets arriving o
|
||||
shortcuts is done with the help of
|
||||
.SM MPOA
|
||||
server
|
||||
-.SM(MPS).
|
||||
+.SM (MPS).
|
||||
.PP
|
||||
Just as the Linux
|
||||
.SM LAN
|
||||
--- a/src/led/zeppelin.8
|
||||
+++ b/src/led/zeppelin.8
|
||||
@@ -99,7 +99,7 @@ Ring and ATM parts of the ELAN, so using
|
||||
recommended. Token Ring support has received less testing than its
|
||||
Ethernet counterpart.
|
||||
.SH FILES
|
||||
-.IP \fI/var/run/lec[interface number].pid\fP
|
||||
+.IP \fI/var/run/lec[interface\ number].pid\fP
|
||||
The file containing the process id of zeppelin.
|
||||
.SH BUGS
|
||||
John Bonham died 1980 and Led Zeppelin broke.
|
||||
--- a/src/sigd/atmsigd.conf.4
|
||||
+++ b/src/sigd/atmsigd.conf.4
|
||||
@@ -125,7 +125,7 @@ a comment. The `#' character cannot be e
|
||||
.P
|
||||
If an option is specified in \fBatmsigd.conf\fP and on the command
|
||||
line, the command line has priority.
|
||||
-.COMPATIBILITY
|
||||
+.SH COMPATIBILITY
|
||||
Certain options used by past versions of \fBatmsigd\fP but no longer documented
|
||||
on the man page are still recognized and supported, but they also yield a
|
||||
warning message. Future versions of \fBatmsigd\fP will not recognize those
|
||||
@@ -663,7 +664,7 @@ int no_check = 0;
|
||||
exit(0);
|
||||
|
||||
usage:
|
||||
- fprintf(stderr, Usage);
|
||||
+ fprintf(stderr, "%s", Usage);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
--- a/src/arpd/arp.c
|
||||
+++ b/src/arpd/arp.c
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <netinet/in.h> /* for ntohs, etc. */
|
||||
#define _LINUX_NETDEVICE_H /* very crude hack for glibc2 */
|
||||
#include <linux/types.h>
|
||||
+#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <atm.h>
|
||||
--- a/src/arpd/itf.c
|
||||
+++ b/src/arpd/itf.c
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <sys/socket.h>
|
||||
#define _LINUX_NETDEVICE_H /* glibc2 */
|
||||
#include <linux/types.h>
|
||||
+#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
|
||||
#include "atmd.h"
|
||||
--- a/src/maint/atmdump.c
|
||||
+++ b/src/maint/atmdump.c
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
+#include <linux/sockios.h>
|
||||
#include <netinet/in.h> /* for htonl and ntohl */
|
||||
#include <atm.h>
|
||||
|
||||
--- a/src/maint/saaldump.c
|
||||
+++ b/src/maint/saaldump.c
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
+#include <linux/sockios.h>
|
||||
#include <atm.h>
|
||||
|
||||
#include "pdu.h"
|
||||
@@ -28,8 +28,8 @@ in Linux 4.20.
|
||||
#include <sys/socket.h>
|
||||
-#define _LINUX_NETDEVICE_H /* glibc2 */
|
||||
#include <linux/types.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
|
||||
--- a/src/arpd/io.c
|
||||
+++ b/src/arpd/io.c
|
||||
@@ -21,7 +21,6 @@
|
||||
@@ -48,5 +48,5 @@ in Linux 4.20.
|
||||
#include <netinet/in.h> /* for ntohs, etc. */
|
||||
-#define _LINUX_NETDEVICE_H /* very crude hack for glibc2 */
|
||||
#include <linux/types.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
--- a/src/test/ttcp.c
|
||||
+++ b/src/test/ttcp.c
|
||||
@@ -664,7 +664,7 @@ int no_check = 0;
|
||||
exit(0);
|
||||
|
||||
usage:
|
||||
- fprintf(stderr, Usage);
|
||||
+ fprintf(stderr, "%s", Usage);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
--- a/src/arpd/io.c
|
||||
+++ b/src/arpd/io.c
|
||||
@@ -615,7 +615,7 @@ int ip_itf_info(int number,uint32_t *ip,
|
||||
int get_local(int fd,struct sockaddr_atmsvc *addr)
|
||||
{
|
||||
int result;
|
||||
- size_t length;
|
||||
+ socklen_t length;
|
||||
|
||||
length = sizeof(struct sockaddr_atmsvc);
|
||||
result = getsockname(fd,(struct sockaddr *) addr,&length);
|
||||
--- a/src/led/conn.c
|
||||
+++ b/src/led/conn.c
|
||||
@@ -405,7 +405,7 @@ Conn_t *accept_conn(Conn_t *conn)
|
||||
{
|
||||
Conn_t *new;
|
||||
struct sockaddr_atmsvc addr;
|
||||
- size_t len;
|
||||
+ socklen_t len;
|
||||
int fd;
|
||||
char buff[MAX_ATM_ADDR_LEN+1];
|
||||
|
||||
@@ -538,7 +538,7 @@ static int handle_accept(Conn_t *conn)
|
||||
*/
|
||||
static int handle_data(Conn_t *conn)
|
||||
{
|
||||
- char buff[MAX_CTRL_FRAME];
|
||||
+ unsigned char buff[MAX_CTRL_FRAME];
|
||||
int retval;
|
||||
|
||||
retval = recv_frame(conn, buff, sizeof(buff));
|
||||
--- a/src/led/frames.c
|
||||
+++ b/src/led/frames.c
|
||||
@@ -312,7 +312,7 @@ static void handle_ready_ind(Conn_t *con
|
||||
* dependant handler functions.
|
||||
* Returns < 0 for serious error
|
||||
*/
|
||||
-int handle_frame(Conn_t *conn, char *buff, int size)
|
||||
+int handle_frame(Conn_t *conn, unsigned char *buff, int size)
|
||||
{
|
||||
struct ctrl_frame *frame;
|
||||
|
||||
--- a/src/led/frames.h
|
||||
+++ b/src/led/frames.h
|
||||
@@ -13,7 +13,7 @@ int validate_frame(unsigned char *buff,
|
||||
void send_ready_ind(Conn_t *conn);
|
||||
void send_register_req(void);
|
||||
|
||||
-int handle_frame(Conn_t *conn, char *buff, int size);
|
||||
+int handle_frame(Conn_t *conn, unsigned char *buff, int size);
|
||||
uint32_t send_flush_req(Conn_t *conn);
|
||||
|
||||
void parse_tlvs(uint16_t opcode, unsigned char *tlvp, int numtlvs, int sizeoftlvs);
|
||||
--- a/src/led/join.c
|
||||
+++ b/src/led/join.c
|
||||
@@ -43,7 +43,7 @@ static int read_join_rsp(char *buff, int
|
||||
static int parse_join_rsp(unsigned char *buff, int size);
|
||||
|
||||
static int get_bus_addr(struct sockaddr_atmsvc *addr);
|
||||
-static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, char *buff, int buffsize);
|
||||
+static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, unsigned char *buff, int buffsize);
|
||||
|
||||
/*
|
||||
* 5.1, Initial state
|
||||
@@ -693,7 +693,7 @@ static int get_bus_addr(struct sockaddr_
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int n = 0, retval, timeout;
|
||||
- char buff[MAX_CTRL_FRAME];
|
||||
+ unsigned char buff[MAX_CTRL_FRAME];
|
||||
|
||||
timeout = 4; /* wait response for 4 seconds */
|
||||
lec_params.c7c_current_timeout = 1;
|
||||
@@ -740,7 +740,7 @@ static int get_bus_addr(struct sockaddr_
|
||||
* Tries to read BUS ATM address in *addr
|
||||
* returns < 0 for error, 0 for not found > 0 for success
|
||||
*/
|
||||
-static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, char *buff, int buffsize)
|
||||
+static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, unsigned char *buff, int buffsize)
|
||||
{
|
||||
int frame_size;
|
||||
struct ctrl_frame *frame;
|
||||
@@ -0,0 +1,40 @@
|
||||
--- a/src/lane/load.c
|
||||
+++ b/src/lane/load.c
|
||||
@@ -498,8 +498,8 @@ load_vars(const char *file)
|
||||
break;
|
||||
case BOOLEAN:
|
||||
Debug_unit(&load_unit, "Variable is boolean: %s",
|
||||
- g_return.bool==BL_TRUE?"True":"False");
|
||||
- set_var_bool(curr_unit, varname, g_return.bool);
|
||||
+ g_return.boolean==BL_TRUE?"True":"False");
|
||||
+ set_var_bool(curr_unit, varname, g_return.boolean);
|
||||
break;
|
||||
case INTEGER:
|
||||
Debug_unit(&load_unit, "Variable is integer: %d", g_return.intti);
|
||||
--- a/src/lane/load_lex.h
|
||||
+++ b/src/lane/load_lex.h
|
||||
@@ -24,7 +24,7 @@
|
||||
#define END 0
|
||||
|
||||
typedef struct {
|
||||
- Bool_t bool;
|
||||
+ Bool_t boolean;
|
||||
int intti;
|
||||
AtmAddr_t *atmaddress;
|
||||
LaneDestination_t *destaddr;
|
||||
--- a/src/lane/load_lex.l
|
||||
+++ b/src/lane/load_lex.l
|
||||
@@ -44,11 +44,11 @@ H [0-9a-fA-F]
|
||||
return ATMADDRESS;
|
||||
}
|
||||
True |
|
||||
-true {g_return.bool = BL_TRUE;
|
||||
+true {g_return.boolean = BL_TRUE;
|
||||
return BOOLEAN;
|
||||
}
|
||||
False |
|
||||
-false {g_return.bool = BL_FALSE;
|
||||
+false {g_return.boolean = BL_FALSE;
|
||||
return BOOLEAN;
|
||||
}
|
||||
\#.* {}
|
||||
@@ -1,21 +0,0 @@
|
||||
--- a/src/maint/saaldump.c 2020-03-29 22:58:01.089711789 +0200
|
||||
+++ b/src/maint/saaldump.c 2020-03-29 22:59:17.564639387 +0200
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
+#include <linux/sockios.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
--- a/src/maint/atmdump.c 2020-03-29 22:58:18.573694469 +0200
|
||||
+++ b/src/maint/atmdump.c 2020-03-29 22:58:49.956729365 +0200
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
+#include <linux/sockios.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
--- a/src/maint/Makefile.am
|
||||
+++ b/src/maint/Makefile.am
|
||||
@@ -1,5 +1,5 @@
|
||||
BOOTPGMS=atmaddr esi
|
||||
-SYSPGMS=atmloop atmtcp enitune zntune hediag # nstune
|
||||
+SYSPGMS=atmloop atmtcp enitune hediag # nstune
|
||||
USRPGMS=atmdiag atmdump sonetdiag saaldump
|
||||
|
||||
INCLUDES=-I$(srcdir)/../q2931 -I$(srcdir)/../saal -I.
|
||||
@@ -14,7 +14,6 @@ esi_SOURCES = esi.c
|
||||
atmloop_SOURCES = atmloop.c
|
||||
atmtcp_SOURCES = atmtcp.c
|
||||
enitune_SOURCES = enitune.c
|
||||
-zntune_SOURCES = zntune.c
|
||||
#nstune_SOURCES = nstune.c
|
||||
|
||||
atmdiag_SOURCES = atmdiag.c
|
||||
+1
-1
@@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}")
|
||||
# - pkg/version/current.go
|
||||
#
|
||||
# Use `tools/bump_version.sh` script to change all those files at one shot.
|
||||
VERSION="3.26.0"
|
||||
VERSION="3.26.1"
|
||||
|
||||
# With .ONESHELL, each recipe is executed in a single shell instance.
|
||||
# This allows `cd` to affect subsequent commands in the same recipe.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: mieru
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: mieru
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy client
|
||||
License: GPLv3+
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: mieru
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: arm64
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: mieru
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy client
|
||||
License: GPLv3+
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: mita
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: mita
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy server
|
||||
License: GPLv3+
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: mita
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: arm64
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: mita
|
||||
Version: 3.26.0
|
||||
Version: 3.26.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Mieru proxy server
|
||||
License: GPLv3+
|
||||
|
||||
@@ -18,32 +18,32 @@ Or you can manually install and configure proxy server using the steps below.
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_amd64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_arm64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.x86_64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.aarch64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
## Install mita package
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
sudo dpkg -i mita_3.26.0_amd64.deb
|
||||
sudo dpkg -i mita_3.26.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
sudo dpkg -i mita_3.26.0_arm64.deb
|
||||
sudo dpkg -i mita_3.26.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
sudo rpm -Uvh --force mita-3.26.0-1.x86_64.rpm
|
||||
sudo rpm -Uvh --force mita-3.26.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
sudo rpm -Uvh --force mita-3.26.0-1.aarch64.rpm
|
||||
sudo rpm -Uvh --force mita-3.26.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
Those instructions can also be used to upgrade the version of mita software package.
|
||||
|
||||
@@ -18,32 +18,32 @@ sudo python3 setup.py --lang=zh
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_amd64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_arm64.deb
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.x86_64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.aarch64.rpm
|
||||
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
## 安装 mita 软件包
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu - X86_64
|
||||
sudo dpkg -i mita_3.26.0_amd64.deb
|
||||
sudo dpkg -i mita_3.26.1_amd64.deb
|
||||
|
||||
# Debian / Ubuntu - ARM 64
|
||||
sudo dpkg -i mita_3.26.0_arm64.deb
|
||||
sudo dpkg -i mita_3.26.1_arm64.deb
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - X86_64
|
||||
sudo rpm -Uvh --force mita-3.26.0-1.x86_64.rpm
|
||||
sudo rpm -Uvh --force mita-3.26.1-1.x86_64.rpm
|
||||
|
||||
# RedHat / CentOS / Rocky Linux - ARM 64
|
||||
sudo rpm -Uvh --force mita-3.26.0-1.aarch64.rpm
|
||||
sudo rpm -Uvh --force mita-3.26.1-1.aarch64.rpm
|
||||
```
|
||||
|
||||
上述指令也可以用来升级 mita 软件包的版本。
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
package version
|
||||
|
||||
const (
|
||||
AppVersion = "3.26.0"
|
||||
AppVersion = "3.26.1"
|
||||
)
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=filebrowser
|
||||
PKG_VERSION:=2.53.0
|
||||
PKG_VERSION:=2.53.1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/filebrowser/filebrowser/tar.gz/v${PKG_VERSION}?
|
||||
PKG_HASH:=7abd0260e8a5fd1fe29477939e2613b0b9164b55b1706d3cc2682048354b38d1
|
||||
PKG_HASH:=68f55a90cf25c4e147dc50d45de2a619f4b24e9c2f6fa7c7de05130ca0b4e123
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
@@ -869,23 +869,29 @@ function gen_config(var)
|
||||
fallbackTag = fallback_node_tag,
|
||||
strategy = strategy
|
||||
})
|
||||
if _node.balancingStrategy == "leastPing" and not observatory then
|
||||
observatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
enableConcurrency = true
|
||||
}
|
||||
elseif _node.balancingStrategy == "leastLoad" and not burstObservatory then
|
||||
burstObservatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
pingConfig = {
|
||||
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
sampling = 3,
|
||||
timeout = "5s"
|
||||
}
|
||||
}
|
||||
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
|
||||
if _node.balancingStrategy == "leastLoad" then
|
||||
if not burstObservatory then
|
||||
burstObservatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
pingConfig = {
|
||||
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
sampling = 3,
|
||||
timeout = "5s"
|
||||
}
|
||||
}
|
||||
end
|
||||
else
|
||||
if not observatory then
|
||||
observatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
enableConcurrency = true
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
local inbound_tag = gen_loopback(loopback_tag, loopback_dst)
|
||||
table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag })
|
||||
|
||||
Generated
+18
-18
@@ -140,7 +140,7 @@ version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -151,7 +151,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -545,18 +545,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.53"
|
||||
version = "4.5.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.53"
|
||||
version = "4.5.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -931,7 +931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1436,7 +1436,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots 1.0.4",
|
||||
"webpki-roots 1.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1806,9 +1806,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.178"
|
||||
version = "0.2.179"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -2500,7 +2500,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2 0.5.10",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2655,7 +2655,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 1.0.4",
|
||||
"webpki-roots 1.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2767,7 +2767,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3172,7 +3172,7 @@ dependencies = [
|
||||
"tokio-rustls",
|
||||
"trait-variant",
|
||||
"tun",
|
||||
"webpki-roots 1.0.4",
|
||||
"webpki-roots 1.0.5",
|
||||
"windows-sys 0.61.2",
|
||||
"zstd",
|
||||
]
|
||||
@@ -3442,7 +3442,7 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4030,14 +4030,14 @@ version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.4",
|
||||
"webpki-roots 1.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
|
||||
checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=brook
|
||||
PKG_VERSION:=20260101
|
||||
PKG_VERSION:=20260101.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=70e8310f31cef3b80e1696f364f12b2ab2aa0f1fb9fde00f25bda7620c21f096
|
||||
PKG_HASH:=8ddba4ed9ae9d10928e169f8121c6791e0e3c2907fa27d6e0055fe434f6e700e
|
||||
|
||||
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
|
||||
@@ -869,23 +869,29 @@ function gen_config(var)
|
||||
fallbackTag = fallback_node_tag,
|
||||
strategy = strategy
|
||||
})
|
||||
if _node.balancingStrategy == "leastPing" and not observatory then
|
||||
observatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
enableConcurrency = true
|
||||
}
|
||||
elseif _node.balancingStrategy == "leastLoad" and not burstObservatory then
|
||||
burstObservatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
pingConfig = {
|
||||
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
sampling = 3,
|
||||
timeout = "5s"
|
||||
}
|
||||
}
|
||||
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
|
||||
if _node.balancingStrategy == "leastLoad" then
|
||||
if not burstObservatory then
|
||||
burstObservatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
pingConfig = {
|
||||
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
sampling = 3,
|
||||
timeout = "5s"
|
||||
}
|
||||
}
|
||||
end
|
||||
else
|
||||
if not observatory then
|
||||
observatory = {
|
||||
subjectSelector = { "blc-" },
|
||||
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
|
||||
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
|
||||
enableConcurrency = true
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
local inbound_tag = gen_loopback(loopback_tag, loopback_dst)
|
||||
table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag })
|
||||
|
||||
@@ -21,13 +21,13 @@ define Download/geoip
|
||||
HASH:=6878dbacfb1fcb1ee022f63ed6934bcefc95a3c4ba10c88f1131fb88dbf7c337
|
||||
endef
|
||||
|
||||
GEOSITE_VER:=20260102111739
|
||||
GEOSITE_VER:=20260103044301
|
||||
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
|
||||
define Download/geosite
|
||||
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
|
||||
URL_FILE:=dlc.dat
|
||||
FILE:=$(GEOSITE_FILE)
|
||||
HASH:=a535bde603510e96f2623ba081865a63ea2aae6d23c14c3a4430805bcd300f4f
|
||||
HASH:=4998c4fd23f556e588658e4dbd153c728a0da913c3d336d8000ba5920a189ab3
|
||||
endef
|
||||
|
||||
GEOSITE_IRAN_VER:=202512290048
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "5.42.0"
|
||||
version = "5.43.0"
|
||||
build = "Custom"
|
||||
codename = "V2Fly, a community-driven edition of V2Ray."
|
||||
intro = "A unified platform for anti-censorship."
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
<PackageVersion Include="Downloader" Version="4.0.3" />
|
||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1" />
|
||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
|
||||
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
||||
<PackageVersion Include="ReactiveUI" Version="22.3.1" />
|
||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageVersion Include="ReactiveUI.WPF" Version="22.3.1" />
|
||||
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.1" />
|
||||
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.2" />
|
||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.1" />
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.2" />
|
||||
<PackageVersion Include="NLog" Version="6.0.7" />
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||
|
||||
@@ -197,7 +197,7 @@ public partial class CoreConfigV2rayService
|
||||
|
||||
if (item.OutboundTag == Global.DirectTag)
|
||||
{
|
||||
if (normalizedDomain.StartsWith("geosite:"))
|
||||
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||
{
|
||||
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
else if (item.OutboundTag != Global.BlockTag)
|
||||
{
|
||||
if (normalizedDomain.StartsWith("geosite:"))
|
||||
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||
{
|
||||
proxyGeositeList.Add(normalizedDomain);
|
||||
}
|
||||
|
||||
@@ -564,7 +564,7 @@ public partial class CoreConfigV2rayService
|
||||
var fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
protocol = "freedom",
|
||||
tag = $"{Global.ProxyTag}3",
|
||||
tag = $"frag-{Global.ProxyTag}",
|
||||
settings = new()
|
||||
{
|
||||
fragment = new()
|
||||
|
||||
@@ -198,6 +198,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
{
|
||||
if (!it.AllowTest)
|
||||
{
|
||||
await UpdateFunc(it.IndexId, ResUI.SpeedtestingSkip);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ dependencies {
|
||||
// Data and Storage Libraries
|
||||
implementation(libs.mmkv.static)
|
||||
implementation(libs.gson)
|
||||
implementation(libs.okhttp)
|
||||
|
||||
// Reactive and Utility Libraries
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
|
||||
@@ -146,6 +146,9 @@
|
||||
<activity
|
||||
android:name=".ui.CheckUpdateActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.BackupActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.AboutActivity"
|
||||
android:exported="false" />
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
data class WebDavConfig(
|
||||
val baseUrl: String,
|
||||
val username: String? = null,
|
||||
val password: String? = null,
|
||||
val remoteBasePath: String = "/",
|
||||
val timeoutSeconds: Long = 30
|
||||
)
|
||||
@@ -4,6 +4,7 @@ import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig.PREF_IS_BOOTED
|
||||
import com.v2ray.ang.AppConfig.PREF_ROUTING_RULESET
|
||||
import com.v2ray.ang.dto.AssetUrlItem
|
||||
import com.v2ray.ang.dto.WebDavConfig
|
||||
import com.v2ray.ang.dto.ProfileItem
|
||||
import com.v2ray.ang.dto.RulesetItem
|
||||
import com.v2ray.ang.dto.ServerAffiliationInfo
|
||||
@@ -27,6 +28,7 @@ object MmkvManager {
|
||||
private const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
|
||||
private const val KEY_ANG_CONFIGS = "ANG_CONFIGS"
|
||||
private const val KEY_SUB_IDS = "SUB_IDS"
|
||||
private const val KEY_WEBDAV_CONFIG = "WEBDAV_CONFIG"
|
||||
|
||||
//private val profileStorage by lazy { MMKV.mmkvWithID(ID_PROFILE_CONFIG, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
@@ -468,6 +470,7 @@ object MmkvManager {
|
||||
|
||||
//endregion
|
||||
|
||||
//region settings
|
||||
/**
|
||||
* Encodes the settings.
|
||||
*
|
||||
@@ -588,4 +591,22 @@ object MmkvManager {
|
||||
|
||||
//endregion
|
||||
|
||||
//region WebDAV
|
||||
|
||||
/**
|
||||
* Encodes the WebDAV config as JSON into settings storage.
|
||||
*/
|
||||
fun encodeWebDavConfig(config: WebDavConfig): Boolean {
|
||||
return settingsStorage.encode(KEY_WEBDAV_CONFIG, JsonUtil.toJson(config))
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the WebDAV config from settings storage.
|
||||
*/
|
||||
fun decodeWebDavConfig(): WebDavConfig? {
|
||||
val json = settingsStorage.decodeString(KEY_WEBDAV_CONFIG) ?: return null
|
||||
return JsonUtil.fromJson(json, WebDavConfig::class.java)
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.v2ray.ang.handler
|
||||
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.dto.WebDavConfig
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object WebDavManager {
|
||||
private var cfg: WebDavConfig? = null
|
||||
private var client: OkHttpClient? = null
|
||||
|
||||
/**
|
||||
* Initialize the WebDAV manager with a configuration and build an OkHttp client.
|
||||
*
|
||||
* @param config WebDavConfig containing baseUrl, credentials, remoteBasePath and timeoutSeconds.
|
||||
*/
|
||||
fun init(config: WebDavConfig) {
|
||||
cfg = config
|
||||
client = OkHttpClient.Builder()
|
||||
.connectTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
|
||||
.readTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
|
||||
.writeTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
|
||||
.callTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a local file to a remote relative path under the configured remoteBasePath.
|
||||
* The provided `remoteRelativePath` should be relative (e.g. "backup_ng.zip").
|
||||
* The method will attempt to create parent directories via MKCOL before PUT.
|
||||
*
|
||||
* @param localFile File to upload.
|
||||
* @param remoteRelativePath Remote path relative to configured remoteBasePath.
|
||||
* @return true if upload succeeded (HTTP 2xx), false otherwise.
|
||||
*/
|
||||
suspend fun uploadFile(localFile: File, remoteRelativePath: String): Boolean = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val cl = client ?: return@withContext false
|
||||
val remote = buildRemoteUrl(remoteRelativePath)
|
||||
|
||||
// Ensure parent directories exist
|
||||
val dirPath = remote.substringBeforeLast('/')
|
||||
if (dirPath != remote) {
|
||||
ensureRemoteDirs(dirPath)
|
||||
}
|
||||
|
||||
// Determine content type based on file extension
|
||||
val mediaType = when (localFile.extension.lowercase()) {
|
||||
"zip" -> "application/zip"
|
||||
"json" -> "application/json"
|
||||
"txt" -> "text/plain"
|
||||
else -> "application/octet-stream"
|
||||
}.toMediaTypeOrNull()
|
||||
|
||||
val body = localFile.asRequestBody(mediaType)
|
||||
val req = applyAuth(Request.Builder().url(remote).put(body)).build()
|
||||
cl.newCall(req).execute().use { resp ->
|
||||
val success = resp.isSuccessful
|
||||
if (success) {
|
||||
Log.i(AppConfig.TAG, "WebDAV upload success: $remoteRelativePath")
|
||||
} else {
|
||||
Log.e(AppConfig.TAG, "WebDAV upload failed: $remoteRelativePath (HTTP ${resp.code})")
|
||||
}
|
||||
return@withContext success
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "WebDAV upload exception: $remoteRelativePath", e)
|
||||
return@withContext false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a remote file (relative to configured remoteBasePath) into a local file.
|
||||
*
|
||||
* @param remoteRelativePath Remote path relative to configured remoteBasePath.
|
||||
* @param destFile Local destination file to write to.
|
||||
* @return true if download and write succeeded, false otherwise.
|
||||
*/
|
||||
suspend fun downloadFile(remoteRelativePath: String, destFile: File): Boolean = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val cl = client ?: return@withContext false
|
||||
val remote = buildRemoteUrl(remoteRelativePath)
|
||||
val req = applyAuth(Request.Builder().url(remote).get()).build()
|
||||
cl.newCall(req).execute().use { resp ->
|
||||
if (!resp.isSuccessful) {
|
||||
Log.e(AppConfig.TAG, "WebDAV download failed: $remoteRelativePath (HTTP ${resp.code})")
|
||||
return@withContext false
|
||||
}
|
||||
|
||||
val body = resp.body
|
||||
if (body == null) {
|
||||
Log.e(AppConfig.TAG, "WebDAV download failed: $remoteRelativePath (empty response body)")
|
||||
return@withContext false
|
||||
}
|
||||
|
||||
body.byteStream().use { input ->
|
||||
destFile.parentFile?.mkdirs()
|
||||
FileOutputStream(destFile).use { fos ->
|
||||
input.copyTo(fos)
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(AppConfig.TAG, "WebDAV download success: $remoteRelativePath")
|
||||
return@withContext true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "WebDAV download exception: $remoteRelativePath", e)
|
||||
return@withContext false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a full remote URL by combining the configured base URL, the configured
|
||||
* remote base path and a relative path provided by the caller.
|
||||
*
|
||||
* Example: baseUrl="https://example.com/remote.php/dav", remoteBasePath="backups",
|
||||
* remoteRelativePath="backup_ng.zip" => "https://example.com/remote.php/dav/backups/backup_ng.zip"
|
||||
*
|
||||
* @param remoteRelativePath A path relative to the configured remoteBasePath (no leading slash required).
|
||||
* @return Full URL string used for HTTP operations.
|
||||
*/
|
||||
private fun buildRemoteUrl(remoteRelativePath: String): String {
|
||||
val base = cfg?.baseUrl?.trimEnd('/') ?: ""
|
||||
val basePath = cfg?.remoteBasePath?.trim('/') ?: ""
|
||||
val rel = remoteRelativePath.trimStart('/')
|
||||
return if (basePath.isEmpty()) "$base/$rel" else "$base/$basePath/$rel"
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply HTTP Basic authentication headers to the given request builder when
|
||||
* username is configured in `cfg`.
|
||||
*
|
||||
* @param builder OkHttp Request.Builder to modify.
|
||||
* @return The same builder instance with Authorization header applied if credentials exist.
|
||||
*/
|
||||
private fun applyAuth(builder: Request.Builder): Request.Builder {
|
||||
val username = cfg?.username
|
||||
val password = cfg?.password
|
||||
if (!username.isNullOrEmpty()) {
|
||||
builder.header("Authorization", Credentials.basic(username, password ?: ""))
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that each directory segment in the given directory URL exists on the
|
||||
* WebDAV server. This issues MKCOL requests for each segment in a best-effort
|
||||
* manner and ignores errors for segments that already exist.
|
||||
*
|
||||
* @param dirUrl Absolute URL to the directory that should exist (e.g. https://.../backups)
|
||||
*/
|
||||
private fun ensureRemoteDirs(dirUrl: String) {
|
||||
try {
|
||||
val cl = client ?: return
|
||||
val url = URL(dirUrl)
|
||||
val segments = url.path.split("/").filter { it.isNotEmpty() }
|
||||
var accum = ""
|
||||
for (seg in segments) {
|
||||
accum += "/$seg"
|
||||
val mkUrl = URL(url.protocol, url.host, if (url.port == -1) -1 else url.port, accum).toString()
|
||||
try {
|
||||
val req = applyAuth(Request.Builder().url(mkUrl).method("MKCOL", null)).build()
|
||||
cl.newCall(req).execute().use { resp ->
|
||||
// 201 Created or 405 Method Not Allowed (already exists) are acceptable
|
||||
if (resp.code != 201 && resp.code != 405 && resp.code != 409) {
|
||||
Log.w(AppConfig.TAG, "WebDAV MKCOL $mkUrl returned ${resp.code}")
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
// best-effort, continue
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "WebDAV ensureRemoteDirs error", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +1,22 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivityAboutBinding
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.extension.toastError
|
||||
import com.v2ray.ang.extension.toastSuccess
|
||||
import com.v2ray.ang.handler.SpeedtestManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import com.v2ray.ang.util.ZipUtil
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class AboutActivity : BaseActivity() {
|
||||
|
||||
private val binding by lazy { ActivityAboutBinding.inflate(layoutInflater) }
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
try {
|
||||
showFileChooser()
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
private val createBackupFile =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
val ret = backupConfigurationToCache()
|
||||
if (ret.first) {
|
||||
// Copy the cached zip file to user-selected location
|
||||
contentResolver.openOutputStream(uri)?.use { output ->
|
||||
File(ret.second).inputStream().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
// Clean up cache file
|
||||
File(ret.second).delete()
|
||||
toastSuccess(R.string.toast_success)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to backup configuration", e)
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
||||
title = getString(R.string.title_about)
|
||||
|
||||
binding.layoutBackup.setOnClickListener {
|
||||
val dateFormatted = SimpleDateFormat(
|
||||
"yyyy-MM-dd-HH-mm-ss",
|
||||
Locale.getDefault()
|
||||
).format(System.currentTimeMillis())
|
||||
val defaultFileName = "${getString(R.string.app_name)}_${dateFormatted}.zip"
|
||||
|
||||
// Let user choose where to save the backup file
|
||||
createBackupFile.launch(defaultFileName)
|
||||
}
|
||||
|
||||
binding.layoutShare.setOnClickListener {
|
||||
val ret = backupConfigurationToCache()
|
||||
if (ret.first) {
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
Intent(Intent.ACTION_SEND).setType("application/zip")
|
||||
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(
|
||||
Intent.EXTRA_STREAM,
|
||||
FileProvider.getUriForFile(
|
||||
this, BuildConfig.APPLICATION_ID + ".cache", File(ret.second)
|
||||
)
|
||||
), getString(R.string.title_configuration_share)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
|
||||
binding.layoutRestore.setOnClickListener {
|
||||
val permission =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
showFileChooser()
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
|
||||
}
|
||||
} else {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
}
|
||||
|
||||
binding.layoutSoureCcode.setOnClickListener {
|
||||
Utils.openUri(this, AppConfig.APP_URL)
|
||||
}
|
||||
@@ -154,78 +50,4 @@ class AboutActivity : BaseActivity() {
|
||||
binding.tvAppId.text = it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup configuration to cache directory
|
||||
* Returns Pair<success, zipFilePath>
|
||||
*/
|
||||
private fun backupConfigurationToCache(): Pair<Boolean, String> {
|
||||
val dateFormatted = SimpleDateFormat(
|
||||
"yyyy-MM-dd-HH-mm-ss",
|
||||
Locale.getDefault()
|
||||
).format(System.currentTimeMillis())
|
||||
val folderName = "${getString(R.string.app_name)}_${dateFormatted}"
|
||||
val backupDir = this.cacheDir.absolutePath + "/$folderName"
|
||||
val outputZipFilePath = "${this.cacheDir.absolutePath}/$folderName.zip"
|
||||
|
||||
val count = MMKV.backupAllToDirectory(backupDir)
|
||||
if (count <= 0) {
|
||||
return Pair(false, "")
|
||||
}
|
||||
|
||||
if (ZipUtil.zipFromFolder(backupDir, outputZipFilePath)) {
|
||||
return Pair(true, outputZipFilePath)
|
||||
} else {
|
||||
return Pair(false, "")
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreConfiguration(zipFile: File): Boolean {
|
||||
val backupDir = this.cacheDir.absolutePath + "/${System.currentTimeMillis()}"
|
||||
|
||||
if (!ZipUtil.unzipToFolder(zipFile, backupDir)) {
|
||||
return false
|
||||
}
|
||||
|
||||
val count = MMKV.restoreAllFromDirectory(backupDir)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
private fun showFileChooser() {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "*/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
}
|
||||
|
||||
try {
|
||||
chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
|
||||
} catch (ex: android.content.ActivityNotFoundException) {
|
||||
Log.e(AppConfig.TAG, "File chooser activity not found", ex)
|
||||
toast(R.string.toast_require_file_manager)
|
||||
}
|
||||
}
|
||||
|
||||
private val chooseFile =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val uri = result.data?.data
|
||||
if (result.resultCode == RESULT_OK && uri != null) {
|
||||
try {
|
||||
val targetFile =
|
||||
File(this.cacheDir.absolutePath, "${System.currentTimeMillis()}.zip")
|
||||
contentResolver.openInputStream(uri).use { input ->
|
||||
targetFile.outputStream().use { fileOut ->
|
||||
input?.copyTo(fileOut)
|
||||
}
|
||||
}
|
||||
if (restoreConfiguration(targetFile)) {
|
||||
toastSuccess(R.string.toast_success)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Error during file restore", e)
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AlertDialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.extension.toastError
|
||||
import com.v2ray.ang.extension.toastSuccess
|
||||
import com.v2ray.ang.handler.WebDavManager
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.dto.WebDavConfig
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.v2ray.ang.databinding.ActivityBackupBinding
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import com.v2ray.ang.databinding.DialogWebdavBinding
|
||||
import com.v2ray.ang.util.ZipUtil
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class BackupActivity : BaseActivity() {
|
||||
private val binding by lazy { ActivityBackupBinding.inflate(layoutInflater) }
|
||||
|
||||
private val config_backup_options: Array<out String> by lazy {
|
||||
resources.getStringArray(R.array.config_backup_options)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BACKUP_FILE_NAME = "backup_ng.zip"
|
||||
}
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
try {
|
||||
showFileChooser()
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
private val createBackupFile =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
val ret = backupConfigurationToCache()
|
||||
if (ret.first) {
|
||||
// Copy the cached zip file to user-selected location
|
||||
contentResolver.openOutputStream(uri)?.use { output ->
|
||||
File(ret.second).inputStream().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
// Clean up cache file
|
||||
File(ret.second).delete()
|
||||
toastSuccess(R.string.toast_success)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to backup configuration", e)
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
||||
title = getString(R.string.title_configuration_backup_restore)
|
||||
|
||||
binding.layoutBackup.setOnClickListener {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.title_configuration_backup)
|
||||
.setItems(config_backup_options) { dialog, which ->
|
||||
when (which) {
|
||||
0 -> backupViaLocal()
|
||||
1 -> backupViaWebDav()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.layoutShare.setOnClickListener {
|
||||
val ret = backupConfigurationToCache()
|
||||
if (ret.first) {
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
Intent(Intent.ACTION_SEND).setType("application/zip")
|
||||
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(
|
||||
Intent.EXTRA_STREAM,
|
||||
FileProvider.getUriForFile(
|
||||
this, BuildConfig.APPLICATION_ID + ".cache", File(ret.second)
|
||||
)
|
||||
), getString(R.string.title_configuration_share)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
|
||||
binding.layoutRestore.setOnClickListener {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.title_configuration_restore)
|
||||
.setItems(config_backup_options) { dialog, which ->
|
||||
when (which) {
|
||||
0 -> restoreViaLocal()
|
||||
1 -> restoreViaWebDav()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.layoutWebdavConfigSetting.setOnClickListener {
|
||||
showWebDavSettingsDialog()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup configuration to cache directory
|
||||
* Returns Pair<success, zipFilePath>
|
||||
*/
|
||||
private fun backupConfigurationToCache(): Pair<Boolean, String> {
|
||||
val dateFormatted = SimpleDateFormat(
|
||||
"yyyy-MM-dd-HH-mm-ss",
|
||||
Locale.getDefault()
|
||||
).format(System.currentTimeMillis())
|
||||
val folderName = "${getString(R.string.app_name)}_${dateFormatted}"
|
||||
val backupDir = this.cacheDir.absolutePath + "/$folderName"
|
||||
val outputZipFilePath = "${this.cacheDir.absolutePath}/$folderName.zip"
|
||||
|
||||
val count = MMKV.backupAllToDirectory(backupDir)
|
||||
if (count <= 0) {
|
||||
return Pair(false, "")
|
||||
}
|
||||
|
||||
if (ZipUtil.zipFromFolder(backupDir, outputZipFilePath)) {
|
||||
return Pair(true, outputZipFilePath)
|
||||
} else {
|
||||
return Pair(false, "")
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreConfiguration(zipFile: File): Boolean {
|
||||
val backupDir = this.cacheDir.absolutePath + "/${System.currentTimeMillis()}"
|
||||
|
||||
if (!ZipUtil.unzipToFolder(zipFile, backupDir)) {
|
||||
return false
|
||||
}
|
||||
|
||||
val count = MMKV.restoreAllFromDirectory(backupDir)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
private fun showFileChooser() {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "*/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
}
|
||||
|
||||
try {
|
||||
chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
|
||||
} catch (ex: ActivityNotFoundException) {
|
||||
Log.e(AppConfig.TAG, "File chooser activity not found", ex)
|
||||
toast(R.string.toast_require_file_manager)
|
||||
}
|
||||
}
|
||||
|
||||
private val chooseFile =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val uri = result.data?.data
|
||||
if (result.resultCode == RESULT_OK && uri != null) {
|
||||
try {
|
||||
val targetFile =
|
||||
File(this.cacheDir.absolutePath, "${System.currentTimeMillis()}.zip")
|
||||
contentResolver.openInputStream(uri).use { input ->
|
||||
targetFile.outputStream().use { fileOut ->
|
||||
input?.copyTo(fileOut)
|
||||
}
|
||||
}
|
||||
if (restoreConfiguration(targetFile)) {
|
||||
toastSuccess(R.string.toast_success)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Error during file restore", e)
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupViaLocal() {
|
||||
val dateFormatted = SimpleDateFormat(
|
||||
"yyyy-MM-dd-HH-mm-ss",
|
||||
Locale.getDefault()
|
||||
).format(System.currentTimeMillis())
|
||||
val defaultFileName = "${getString(R.string.app_name)}_${dateFormatted}.zip"
|
||||
createBackupFile.launch(defaultFileName)
|
||||
}
|
||||
|
||||
private fun restoreViaLocal() {
|
||||
val permission =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
showFileChooser()
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
|
||||
}
|
||||
} else {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupViaWebDav() {
|
||||
val saved = MmkvManager.decodeWebDavConfig()
|
||||
if (saved == null || saved.baseUrl.isEmpty()) {
|
||||
toastError(R.string.title_webdav_config_setting_unknown)
|
||||
return
|
||||
}
|
||||
|
||||
binding.pbWaiting.show()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
var tempFile: File? = null
|
||||
try {
|
||||
val ret = backupConfigurationToCache()
|
||||
if (!ret.first) {
|
||||
withContext(Dispatchers.Main) {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
tempFile = File(ret.second)
|
||||
WebDavManager.init(saved)
|
||||
|
||||
val ok = try {
|
||||
WebDavManager.uploadFile(tempFile, BACKUP_FILE_NAME)
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "WebDAV upload error", e)
|
||||
false
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ok) toastSuccess(R.string.toast_success) else toastError(R.string.toast_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "WebDAV backup error", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
tempFile?.delete()
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.pbWaiting.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreViaWebDav() {
|
||||
val saved = MmkvManager.decodeWebDavConfig()
|
||||
if (saved == null || saved.baseUrl.isEmpty()) {
|
||||
toastError(R.string.title_webdav_config_setting_unknown)
|
||||
return
|
||||
}
|
||||
|
||||
binding.pbWaiting.show()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
var target: File? = null
|
||||
try {
|
||||
target = File(cacheDir, "download_${System.currentTimeMillis()}.zip")
|
||||
WebDavManager.init(saved)
|
||||
val ok = WebDavManager.downloadFile(BACKUP_FILE_NAME, target)
|
||||
if (!ok) {
|
||||
withContext(Dispatchers.Main) {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
val restored = restoreConfiguration(target)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (restored) {
|
||||
setResult(RESULT_OK, Intent().putExtra("config_restored", true))
|
||||
toastSuccess(R.string.toast_success)
|
||||
} else {
|
||||
toastError(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "WebDAV download error", e)
|
||||
withContext(Dispatchers.Main) { toastError(R.string.toast_failure) }
|
||||
} finally {
|
||||
try {
|
||||
target?.delete()
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.pbWaiting.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showWebDavSettingsDialog() {
|
||||
val dialogBinding = DialogWebdavBinding.inflate(layoutInflater)
|
||||
|
||||
MmkvManager.decodeWebDavConfig()?.let { cfg ->
|
||||
dialogBinding.etWebdavUrl.setText(cfg.baseUrl)
|
||||
dialogBinding.etWebdavUser.setText(cfg.username ?: "")
|
||||
dialogBinding.etWebdavPass.setText(cfg.password ?: "")
|
||||
dialogBinding.etWebdavRemotePath.setText(cfg.remoteBasePath ?: "/")
|
||||
}
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.title_webdav_config_setting)
|
||||
.setView(dialogBinding.root)
|
||||
.setPositiveButton(R.string.menu_item_save_config) { _, _ ->
|
||||
val url = dialogBinding.etWebdavUrl.text.toString().trim()
|
||||
val user = dialogBinding.etWebdavUser.text.toString().trim().ifEmpty { null }
|
||||
val pass = dialogBinding.etWebdavPass.text.toString()
|
||||
val remotePath = dialogBinding.etWebdavRemotePath.text.toString().trim().ifEmpty { "/" }
|
||||
val cfg = WebDavConfig(baseUrl = url, username = user, password = pass, remoteBasePath = remotePath)
|
||||
MmkvManager.encodeWebDavConfig(cfg)
|
||||
toastSuccess(R.string.toast_success)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
startV2Ray()
|
||||
}
|
||||
}
|
||||
private val requestSubSettingActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
private val requestActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
initGroupTab()
|
||||
}
|
||||
private val tabGroupListener = object : TabLayout.OnTabSelectedListener {
|
||||
@@ -672,9 +672,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
// Handle navigation view item clicks here.
|
||||
when (item.itemId) {
|
||||
R.id.sub_setting -> requestSubSettingActivity.launch(Intent(this, SubSettingActivity::class.java))
|
||||
R.id.sub_setting -> requestActivityLauncher.launch(Intent(this, SubSettingActivity::class.java))
|
||||
R.id.per_app_proxy_settings -> startActivity(Intent(this, PerAppProxyActivity::class.java))
|
||||
R.id.routing_setting -> requestSubSettingActivity.launch(Intent(this, RoutingSettingActivity::class.java))
|
||||
R.id.routing_setting -> requestActivityLauncher.launch(Intent(this, RoutingSettingActivity::class.java))
|
||||
R.id.user_asset_setting -> startActivity(Intent(this, UserAssetActivity::class.java))
|
||||
R.id.settings -> startActivity(
|
||||
Intent(this, SettingsActivity::class.java)
|
||||
@@ -684,6 +684,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
R.id.promotion -> Utils.openUri(this, "${Utils.decode(AppConfig.APP_PROMOTION_URL)}?t=${System.currentTimeMillis()}")
|
||||
R.id.logcat -> startActivity(Intent(this, LogcatActivity::class.java))
|
||||
R.id.check_for_update -> startActivity(Intent(this, CheckUpdateActivity::class.java))
|
||||
R.id.backup_restore -> requestActivityLauncher.launch(Intent(this, BackupActivity::class.java))
|
||||
R.id.about -> startActivity(Intent(this, AboutActivity::class.java))
|
||||
}
|
||||
|
||||
|
||||
@@ -9,99 +9,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_backup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_backup_24dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_configuration_backup"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_share"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_share_24dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16"
|
||||
android:text="@string/title_configuration_share"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_restore"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_restore_24dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16"
|
||||
android:text="@string/title_configuration_restore"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/padding_spacing_dp16">
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_soure_ccode"
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/pb_waiting"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible"
|
||||
app:indicatorColor="@color/color_fab_active" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_backup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_backup_24dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_configuration_backup"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_share"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_share_24dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16"
|
||||
android:text="@string/title_configuration_share"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_restore"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_restore_24dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16"
|
||||
android:text="@string/title_configuration_restore"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/padding_spacing_dp16">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_webdav_config_setting"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_settings_24dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_webdav_config_setting"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_webdav_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title_webdav_url"
|
||||
android:inputType="textUri"
|
||||
android:layout_marginTop="@dimen/padding_spacing_dp8" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_webdav_user"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title_webdav_user"
|
||||
android:layout_marginTop="@dimen/padding_spacing_dp8" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_webdav_pass"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title_webdav_pass"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginTop="@dimen/padding_spacing_dp8" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_webdav_remote_path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title_webdav_remote_path"
|
||||
android:layout_marginTop="@dimen/padding_spacing_dp8" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -39,6 +39,10 @@
|
||||
android:id="@+id/check_for_update"
|
||||
android:icon="@drawable/ic_check_update_24dp"
|
||||
android:title="@string/update_check_for_update" />
|
||||
<item
|
||||
android:id="@+id/backup_restore"
|
||||
android:icon="@drawable/ic_restore_24dp"
|
||||
android:title="@string/title_configuration_backup_restore" />
|
||||
<item
|
||||
android:id="@+id/about"
|
||||
android:icon="@drawable/ic_about_24dp"
|
||||
|
||||
@@ -238,9 +238,6 @@
|
||||
<string name="title_source_code">الكود المصدري</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">قناة Telegram</string>
|
||||
<string name="title_configuration_backup">نسخ التكوين احتياطيًا</string>
|
||||
<string name="title_configuration_restore">استعادة التكوين</string>
|
||||
<string name="title_configuration_share">مشاركة التكوين</string>
|
||||
|
||||
<string name="title_pref_promotion">ترويج</string>
|
||||
|
||||
@@ -339,6 +336,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">نسخ التكوين احتياطيًا</string>
|
||||
<string name="title_configuration_restore">استعادة التكوين</string>
|
||||
<string name="title_configuration_share">مشاركة التكوين</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>رمز استجابة سريعة (QRcode)</item>
|
||||
<item>تصدير إلى الحافظة</item>
|
||||
@@ -384,6 +393,10 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -238,9 +238,6 @@
|
||||
<string name="title_source_code">সোর্স কোড</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">টেলিগ্রাম চ্যানেল</string>
|
||||
<string name="title_configuration_backup">কনফিগারেশন ব্যাকআপ</string>
|
||||
<string name="title_configuration_restore">কনফিগারেশন পুনরুদ্ধার</string>
|
||||
<string name="title_configuration_share">কনফিগারেশন শেয়ার করুন</string>
|
||||
|
||||
<string name="title_pref_promotion">প্রচার</string>
|
||||
|
||||
@@ -338,6 +335,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">কনফিগারেশন ব্যাকআপ</string>
|
||||
<string name="title_configuration_restore">কনফিগারেশন পুনরুদ্ধার</string>
|
||||
<string name="title_configuration_share">কনফিগারেশন শেয়ার করুন</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR কোড</item>
|
||||
<item>ক্লিপবোর্ডে রপ্তানি করুন</item>
|
||||
@@ -389,6 +398,10 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
@@ -238,9 +238,6 @@
|
||||
<string name="title_source_code">کود بونچک</string>
|
||||
<string name="title_oss_license">موجوزا کود بونچک</string>
|
||||
<string name="title_tg_channel">تورگه تلگرام</string>
|
||||
<string name="title_configuration_backup">لادراری گرؽڌن ز کانفیگ</string>
|
||||
<string name="title_configuration_restore">وورگندن کانفیگ</string>
|
||||
<string name="title_configuration_share">یک رسۊوی کانفیگ</string>
|
||||
|
||||
<string name="title_pref_promotion">تبلیقات</string>
|
||||
|
||||
@@ -347,6 +344,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">لادراری گرؽڌن ز کانفیگ</string>
|
||||
<string name="title_configuration_restore">وورگندن کانفیگ</string>
|
||||
<string name="title_configuration_share">یک رسۊوی کانفیگ</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>و در کشیڌن من کلیپ بورد</item>
|
||||
@@ -399,6 +408,10 @@
|
||||
<item>کم ترین پینگ</item>
|
||||
<item>کم ترین بار(لود)</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">پؽش هل دامنه…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -237,9 +237,6 @@
|
||||
<string name="title_source_code">کد منبع</string>
|
||||
<string name="title_oss_license">مجوز های منبع باز</string>
|
||||
<string name="title_tg_channel">کانال تلگرام</string>
|
||||
<string name="title_configuration_backup">پشتیبان گیری از پیکربندی</string>
|
||||
<string name="title_configuration_restore">بازیابی پیکربندی</string>
|
||||
<string name="title_configuration_share">اشتراک گذاری پیکربندی</string>
|
||||
<string name="title_pref_promotion">تبلیغات</string>
|
||||
|
||||
<string name="title_pref_auto_update_subscription">به روزرسانی خودکار اشتراک ها</string>
|
||||
@@ -344,6 +341,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">پشتیبان گیری از پیکربندی</string>
|
||||
<string name="title_configuration_restore">بازیابی پیکربندی</string>
|
||||
<string name="title_configuration_share">اشتراک گذاری پیکربندی</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>خروجی گرفتن در کلیپ بورد</item>
|
||||
@@ -398,6 +407,10 @@
|
||||
<item>کمترین پینگ</item>
|
||||
<item>کمترین بار(لود)</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -237,9 +237,6 @@
|
||||
<string name="title_source_code">Исходный код</string>
|
||||
<string name="title_oss_license">Лицензии открытого исходного кода</string>
|
||||
<string name="title_tg_channel">Telegram-канал</string>
|
||||
<string name="title_configuration_backup">Резервирование конфигурации</string>
|
||||
<string name="title_configuration_restore">Восстановление конфигурации</string>
|
||||
<string name="title_configuration_share">Поделиться конфигурацией</string>
|
||||
|
||||
<string name="title_pref_promotion">Содействие</string>
|
||||
|
||||
@@ -346,6 +343,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">Резервирование конфигурации</string>
|
||||
<string name="title_configuration_restore">Восстановление конфигурации</string>
|
||||
<string name="title_configuration_share">Поделиться конфигурацией</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR-код</item>
|
||||
<item>Экспорт в буфер обмена</item>
|
||||
@@ -398,6 +407,10 @@
|
||||
<item>Наименьшая задержка</item>
|
||||
<item>Наименьшая нагрузка</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Предварительное определение домена…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -239,9 +239,6 @@
|
||||
<string name="title_source_code">Mã nguồn</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Kênh Telegram</string>
|
||||
<string name="title_configuration_backup">Sao lưu cấu hình</string>
|
||||
<string name="title_configuration_restore">Khôi phục cấu hình</string>
|
||||
<string name="title_configuration_share">Chia sẻ cấu hình</string>
|
||||
<string name="title_pref_promotion">Quảng bá server</string>
|
||||
|
||||
<string name="title_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký</string>
|
||||
@@ -340,6 +337,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">Sao lưu cấu hình</string>
|
||||
<string name="title_configuration_restore">Khôi phục cấu hình</string>
|
||||
<string name="title_configuration_share">Chia sẻ cấu hình</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
|
||||
<item>Sao chép vào Clipboard</item>
|
||||
@@ -386,6 +395,10 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -235,9 +235,6 @@
|
||||
<string name="title_source_code">源代码</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Telegram 频道</string>
|
||||
<string name="title_configuration_backup">备份配置</string>
|
||||
<string name="title_configuration_restore">还原配置</string>
|
||||
<string name="title_configuration_share">分享配置</string>
|
||||
|
||||
<string name="title_pref_promotion">推广</string>
|
||||
|
||||
@@ -338,6 +335,18 @@
|
||||
<string name="title_policy_group_subscription_id">来自订阅分组</string>
|
||||
<string name="title_policy_group_subscription_filter">别名正则过滤</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">备份 & 还原</string>
|
||||
<string name="title_configuration_backup">备份配置</string>
|
||||
<string name="title_configuration_restore">还原配置</string>
|
||||
<string name="title_configuration_share">分享配置</string>
|
||||
<string name="title_webdav_config_setting">WebDAV 设置</string>
|
||||
<string name="title_webdav_config_setting_unknown">请先设置 WebDAV</string>
|
||||
<string name="title_webdav_url">WebDAV 服务器地址</string>WebDAV server address
|
||||
<string name="title_webdav_user">用户名</string>
|
||||
<string name="title_webdav_pass">密码</string>
|
||||
<string name="title_webdav_remote_path">远程文件夹名(可选)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>二维码</item>
|
||||
<item>导出至剪贴板</item>
|
||||
@@ -390,6 +399,10 @@
|
||||
<item>最低延迟</item>
|
||||
<item>最稳定</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">预解析域名中…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>本地</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -236,9 +236,6 @@
|
||||
<string name="title_source_code">原始碼</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Telegram 頻道</string>
|
||||
<string name="title_configuration_backup">備份設定</string>
|
||||
<string name="title_configuration_restore">還原設定</string>
|
||||
<string name="title_configuration_share">分享設定</string>
|
||||
|
||||
<string name="title_pref_promotion">推廣</string>
|
||||
|
||||
@@ -338,6 +335,18 @@
|
||||
<string name="title_policy_group_subscription_id">來自訂閱分組</string>
|
||||
<string name="title_policy_group_subscription_filter">別名正規過濾</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">備份設定</string>
|
||||
<string name="title_configuration_restore">還原設定</string>
|
||||
<string name="title_configuration_share">分享設定</string>
|
||||
<string name="title_webdav_config_setting">WebDAV 設定</string>
|
||||
<string name="title_webdav_config_setting_unknown">請先設定 WebDAV</string>
|
||||
<string name="title_webdav_url">WebDAV 伺服器位址</string>
|
||||
<string name="title_webdav_user">使用者名稱</string>
|
||||
<string name="title_webdav_pass">密碼</string>
|
||||
<string name="title_webdav_remote_path">遠端目錄(可選)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>二維碼</item>
|
||||
<item>匯出至剪貼簿</item>
|
||||
@@ -390,6 +399,10 @@
|
||||
<item>最低延遲</item>
|
||||
<item>最少卡頓</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">預解析域名…</string>
|
||||
|
||||
<string-array name="config_backup_options">
|
||||
<item>本地</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -241,9 +241,6 @@
|
||||
<string name="title_source_code">Source code</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Telegram channel</string>
|
||||
<string name="title_configuration_backup">Backup config</string>
|
||||
<string name="title_configuration_restore">Restore config</string>
|
||||
<string name="title_configuration_share">Share config</string>
|
||||
|
||||
<string name="title_pref_promotion">Promotion</string>
|
||||
|
||||
@@ -350,6 +347,18 @@
|
||||
<string name="title_policy_group_subscription_id">From subscription group</string>
|
||||
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
|
||||
|
||||
<!-- BackupActivity -->
|
||||
<string name="title_configuration_backup_restore">Backup & Restore</string>
|
||||
<string name="title_configuration_backup">Backup config</string>
|
||||
<string name="title_configuration_restore">Restore config</string>
|
||||
<string name="title_configuration_share">Share config</string>
|
||||
<string name="title_webdav_config_setting">WebDAV Settings</string>
|
||||
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
|
||||
<string name="title_webdav_url">WebDAV server URL</string>
|
||||
<string name="title_webdav_user">Username</string>
|
||||
<string name="title_webdav_pass">Password</string>
|
||||
<string name="title_webdav_remote_path">Remote directory (optional)</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>Export to clipboard</item>
|
||||
@@ -404,6 +413,9 @@
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
<string-array name="config_backup_options">
|
||||
<item>Local</item>
|
||||
<item>WebDAV</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -13,6 +13,7 @@ activity = "1.12.2"
|
||||
constraintlayout = "2.2.1"
|
||||
mmkvStatic = "1.3.16"
|
||||
gson = "2.13.2"
|
||||
okhttp = "5.3.2"
|
||||
quickieFoss = "1.14.0"
|
||||
kotlinxCoroutinesAndroid = "1.10.2"
|
||||
kotlinxCoroutinesCore = "1.10.2"
|
||||
@@ -41,6 +42,7 @@ androidx-activity = { group = "androidx.activity", name = "activity", version.re
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
quickie-foss = { module = "com.github.T8RIN.QuickieExtended:quickie-foss", version.ref = "quickieFoss" }
|
||||
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
|
||||
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from .common import InfoExtractor
|
||||
from ..utils import ExtractorError, urlencode_postdata
|
||||
from ..utils import ExtractorError, UserNotLive, urlencode_postdata
|
||||
|
||||
|
||||
class BigoIE(InfoExtractor):
|
||||
@@ -40,7 +40,7 @@ class BigoIE(InfoExtractor):
|
||||
info = info_raw.get('data') or {}
|
||||
|
||||
if not info.get('alive'):
|
||||
raise ExtractorError('This user is offline.', expected=True)
|
||||
raise UserNotLive(video_id=user_id)
|
||||
|
||||
formats, subs = self._extract_m3u8_formats_and_subtitles(
|
||||
info.get('hls_src'), user_id, 'mp4', 'm3u8')
|
||||
|
||||
@@ -680,6 +680,10 @@ class TwitchPlaylistBaseIE(TwitchBaseIE):
|
||||
}],
|
||||
f'Downloading {self._NODE_KIND}s GraphQL page {page_num}',
|
||||
fatal=False)
|
||||
# Avoid extracting random/unrelated entries when channel_name doesn't exist
|
||||
# See https://github.com/yt-dlp/yt-dlp/issues/15450
|
||||
if traverse_obj(page, (0, 'data', 'user', 'id', {str})) == '':
|
||||
raise ExtractorError(f'Channel "{channel_name}" not found', expected=True)
|
||||
if not page:
|
||||
break
|
||||
edges = try_get(
|
||||
|
||||
Reference in New Issue
Block a user