From e836e6eadf3908d62bc73ab87cd193e2779e7dba Mon Sep 17 00:00:00 2001 From: AdingApkgg Date: Sun, 27 Sep 2026 09:18:44 +0800 Subject: [PATCH 01/13] refactor(site): migrate from Valaxy to VitePress Replace Valaxy 0.25.9 + valaxy-theme-press with VitePress 1.6.4, pnpm with Bun 1.4.2, and Node 22 with Node 24 LTS in CI. - Split the single-file bilingual pages into one file per language: Chinese in pages/ (site root), English in pages/en/ (/en/). The split was generated with markdown-it's own parser and checked token by token against the original structure. - Fix container nesting in 8 pages that hid content on the old site (e.g. the English "Notes" in 115_open, the Chinese tip in 189, mixed languages in the worker troubleshooting section) and two dead links. - Move page titles from frontmatter into a leading H1; the sidebar is generated from `categories` / `top` frontmatter in the same order as before. - Port contributors, giscus, BiliBili, download and WIP components, light/dark images, group icons and mermaid (own component, mermaid 12). - Redirect visitors to their preferred language on landing, honouring the old site's saved preference. - Replace Algolia with VitePress local search using Intl.Segmenter tokenization for Chinese. - Update README, CONTRIBUTING and the docs about writing docs. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/gh-pages.yml | 24 +- .gitignore | 35 +- .husky/pre-commit | 2 +- .npmrc | 3 - .nvmrc | 2 +- .prettierignore | 5 +- .vitepress/config.mts | 287 + .vitepress/contributors.ts | 93 + .vitepress/plugins/mermaid.ts | 15 + .../plugins/theme-image.ts | 7 +- .vitepress/sidebar.ts | 121 + .vitepress/theme/Layout.vue | 97 + .../theme/components}/BackToTop.vue | 31 +- .../theme/components}/BiliBili.vue | 2 +- .vitepress/theme/components/Contributors.vue | 77 + .vitepress/theme/components/Mermaid.vue | 82 + .../theme/components}/OpenListDownload.vue | 2 +- .../theme/components/WorkInProgress.vue | 39 + .../components}/openlist_releases_db.json | 0 .vitepress/theme/i18n.ts | 61 + .vitepress/theme/index.ts | 33 + .vitepress/theme/language.ts | 81 + .../index.scss => .vitepress/theme/style.css | 95 +- {utils => .vitepress}/version.ts | 0 CONTRIBUTING.md | 52 +- README.cn.md | 22 +- README.md | 22 +- assets/sites.ts | 1 - bun.lock | 663 ++ components/AddonGallery.vue | 65 - components/AddonGalleryCard.vue | 85 - components/AppLink.vue | 34 - components/AutoLanguageDetector.vue | 26 - components/BrandIcon.vue | 14 - components/CustomVueDemo.vue | 26 - components/InlineIcon.vue | 21 - components/OpenListContributors.vue | 10 - components/OpenListLogo.vue | 44 - components/PressButton.vue | 38 - components/PressGetStarted.vue | 85 - components/PressHome.vue | 21 - components/PressHomeHero.vue | 72 - components/PressNavBar.vue | 109 - components/PressNavScreenMenuLink.vue | 43 - components/ThemeImage.vue | 68 - components/ThemeImagePair.vue | 123 - components/WorkInProgress.vue | 20 - components/api/ApiDocsRedirect.vue | 14 - layouts/default.vue | 95 - layouts/home.vue | 22 - layouts/statement.vue | 55 - locales/en.yml | 76 - locales/zh-CN.yml | 76 - package.json | 51 +- pages/api/apidocs.md | 26 +- pages/configuration/configuration.md | 606 +- pages/configuration/global.md | 276 +- pages/configuration/index.md | 36 +- pages/configuration/other.md | 89 +- pages/configuration/preview.md | 237 +- pages/configuration/side.md | 173 +- pages/configuration/site.md | 113 +- pages/configuration/style.md | 73 +- pages/ecosystem/index.md | 259 +- pages/ecosystem/official_APIpage.md | 386 +- pages/ecosystem/official_desktop.md | 369 +- pages/ecosystem/official_docs.md | 261 +- pages/ecosystem/official_mobile.md | 145 +- pages/ecosystem/official_proxy.md | 119 +- pages/ecosystem/official_worker/about.md | 125 +- pages/ecosystem/official_worker/basic.md | 206 +- pages/ecosystem/official_worker/guide.md | 87 +- pages/ecosystem/official_worker/guide_cfw.md | 116 +- pages/ecosystem/official_worker/guide_env.md | 100 +- pages/ecosystem/official_worker/guide_eom.md | 83 +- pages/ecosystem/official_worker/guide_esa.md | 49 +- pages/ecosystem/official_worker/index.md | 122 +- pages/en/api/apidocs.md | 14 + pages/en/api/index.md | 0 pages/en/configuration/configuration.md | 656 ++ pages/en/configuration/global.md | 177 + pages/en/configuration/index.md | 11 + pages/en/configuration/other.md | 53 + pages/en/configuration/preview.md | 160 + pages/en/configuration/side.md | 136 + pages/en/configuration/site.md | 73 + pages/en/configuration/style.md | 39 + pages/en/ecosystem/index.md | 214 + pages/en/ecosystem/official_APIpage.md | 288 + pages/en/ecosystem/official_desktop.md | 194 + pages/en/ecosystem/official_docs.md | 112 + pages/en/ecosystem/official_mobile.md | 70 + pages/en/ecosystem/official_proxy.md | 87 + pages/en/ecosystem/official_worker/about.md | 87 + pages/en/ecosystem/official_worker/basic.md | 103 + pages/en/ecosystem/official_worker/guide.md | 64 + .../en/ecosystem/official_worker/guide_cfw.md | 107 + .../en/ecosystem/official_worker/guide_env.md | 95 + .../en/ecosystem/official_worker/guide_eom.md | 76 + .../en/ecosystem/official_worker/guide_esa.md | 44 + pages/en/ecosystem/official_worker/index.md | 73 + pages/en/faq/_example.md | 12 + pages/en/faq/error.md | 166 + pages/en/faq/howto.md | 113 + pages/en/faq/index.md | 3 + pages/en/faq/seealso.md | 21 + pages/en/faq/why.md | 305 + pages/en/guide/advanced/2fa.md | 51 + pages/en/guide/advanced/alias.md | 10 + pages/en/guide/advanced/backup.md | 32 + pages/en/guide/advanced/balance.md | 104 + pages/en/guide/advanced/ftp.md | 163 + pages/en/guide/advanced/index.md | 0 pages/en/guide/advanced/ipa.md | 98 + pages/en/guide/advanced/mcp.md | 157 + pages/en/guide/advanced/meta.md | 83 + pages/en/guide/advanced/offline-download.md | 198 + pages/en/guide/advanced/rapid_upload.md | 45 + pages/en/guide/advanced/s3.md | 57 + pages/en/guide/advanced/search.md | 236 + pages/en/guide/advanced/sso.md | 164 + pages/en/guide/advanced/sso/01-GitHub.md | 30 + pages/en/guide/advanced/sso/02-Dingtalk.md | 24 + pages/en/guide/advanced/sso/03-Microsoft.md | 33 + pages/en/guide/advanced/sso/04-Google.md | 36 + pages/en/guide/advanced/sso/05-Casdoor.md | 33 + pages/en/guide/advanced/sso/06-Authentik.md | 49 + pages/en/guide/advanced/sso/07-keycloak.md | 90 + pages/en/guide/advanced/strm.md | 10 + pages/en/guide/advanced/user.md | 75 + pages/en/guide/advanced/webdav.md | 151 + pages/en/guide/drivers/115.md | 384 + pages/en/guide/drivers/115_open.md | 171 + pages/en/guide/drivers/123.md | 154 + pages/en/guide/drivers/123_open.md | 120 + pages/en/guide/drivers/139.md | 219 + pages/en/guide/drivers/163music.md | 51 + pages/en/guide/drivers/189.md | 105 + pages/en/guide/drivers/alias.md | 10 + pages/en/guide/drivers/alias_text.md | 282 + pages/en/guide/drivers/alidoc.md | 78 + pages/en/guide/drivers/aliyundrive_open.md | 287 + pages/en/guide/drivers/autoindex.md | 79 + pages/en/guide/drivers/baidu.md | 266 + pages/en/guide/drivers/baidu_photo.md | 60 + pages/en/guide/drivers/chaoxing.md | 51 + pages/en/guide/drivers/chunk.md | 113 + pages/en/guide/drivers/cloudflare-imgbed.md | 83 + pages/en/guide/drivers/cloudreve.md | 72 + pages/en/guide/drivers/cloudreve_v4.md | 116 + pages/en/guide/drivers/cnb_releases.md | 61 + pages/en/guide/drivers/common.md | 207 + pages/en/guide/drivers/crypt.md | 222 + pages/en/guide/drivers/degoo.md | 55 + pages/en/guide/drivers/develop.md | 11 + pages/en/guide/drivers/doubao.md | 58 + pages/en/guide/drivers/dropbox.md | 119 + pages/en/guide/drivers/emby.md | 54 + pages/en/guide/drivers/febbox.md | 57 + pages/en/guide/drivers/feiji.md | 50 + pages/en/guide/drivers/ftp.md | 47 + pages/en/guide/drivers/github.md | 125 + pages/en/guide/drivers/github_releases.md | 202 + pages/en/guide/drivers/google_drive.md | 179 + pages/en/guide/drivers/google_photos.md | 73 + pages/en/guide/drivers/guangyapan.md | 89 + pages/en/guide/drivers/halalcloud.md | 56 + pages/en/guide/drivers/ilanzou.md | 46 + pages/en/guide/drivers/index.md | 0 pages/en/guide/drivers/kodbox.md | 55 + pages/en/guide/drivers/lanzou.md | 94 + pages/en/guide/drivers/lenovo_nas_share.md | 59 + pages/en/guide/drivers/local.md | 65 + pages/en/guide/drivers/mediafire.md | 30 + pages/en/guide/drivers/mediatrack.md | 46 + pages/en/guide/drivers/mega.md | 64 + pages/en/guide/drivers/misskey.md | 17 + pages/en/guide/drivers/mopan.md | 50 + pages/en/guide/drivers/onedrive.md | 146 + pages/en/guide/drivers/onedrive_app.md | 173 + pages/en/guide/drivers/openlist.md | 96 + pages/en/guide/drivers/pikpak.md | 175 + pages/en/guide/drivers/quark.md | 140 + pages/en/guide/drivers/s3.md | 354 + pages/en/guide/drivers/seafile.md | 74 + pages/en/guide/drivers/sftp.md | 49 + pages/en/guide/drivers/smb.md | 32 + pages/en/guide/drivers/storage-list.md | 0 pages/en/guide/drivers/strm.md | 10 + pages/en/guide/drivers/strm_text.md | 100 + pages/en/guide/drivers/teambition.md | 43 + pages/en/guide/drivers/teldrive.md | 82 + pages/en/guide/drivers/terabox.md | 67 + pages/en/guide/drivers/thunder.md | 496 + pages/en/guide/drivers/uc.md | 85 + pages/en/guide/drivers/url_tree.md | 248 + pages/en/guide/drivers/uss.md | 77 + pages/en/guide/drivers/webdav.md | 85 + pages/en/guide/drivers/weiyun.md | 60 + pages/en/guide/drivers/wopan.md | 78 + pages/en/guide/drivers/wps.md | 48 + pages/en/guide/drivers/yandex.md | 37 + pages/en/guide/index.md | 56 + pages/en/guide/installation/1panel.md | 78 + pages/en/guide/installation/desktop.md | 225 + pages/en/guide/installation/docker.md | 567 ++ pages/en/guide/installation/download.md | 12 + pages/en/guide/installation/index.md | 0 pages/en/guide/installation/koyeb.md | 118 + pages/en/guide/installation/manual.md | 401 + pages/en/guide/installation/paas.md | 105 + pages/en/guide/installation/reverse-proxy.md | 137 + pages/en/guide/installation/script.md | 107 + pages/en/guide/installation/source.md | 108 + pages/en/guide/installation/truenas.md | 88 + pages/en/guide/installation/worker.md | 257 + pages/en/guide/migrate.md | 113 + pages/en/index.md | 30 + pages/en/privacy.md | 21 + pages/en/seeds/background.md | 45 + pages/en/seeds/design.md | 287 + pages/en/seeds/faq.md | 65 + pages/en/seeds/generate.md | 101 + pages/en/seeds/index.md | 26 + pages/en/seeds/rapid-upload.md | 75 + pages/en/seeds/usage.md | 116 + .../en/snippets}/reverse-tip.md | 7 - {snippets => pages/en/snippets}/tos-tip.md | 7 - pages/en/terms.md | 38 + pages/faq/error.md | 204 +- pages/faq/howto.md | 191 +- pages/faq/index.md | 4 +- pages/faq/seealso.md | 31 +- pages/faq/why.md | 560 +- pages/guide/advanced/2fa.md | 63 +- pages/guide/advanced/alias.md | 5 +- pages/guide/advanced/backup.md | 47 +- pages/guide/advanced/balance.md | 125 +- pages/guide/advanced/ftp.md | 223 +- pages/guide/advanced/ipa.md | 137 +- pages/guide/advanced/mcp.md | 244 +- pages/guide/advanced/meta.md | 145 +- pages/guide/advanced/offline-download.md | 131 +- pages/guide/advanced/rapid_upload.md | 81 +- pages/guide/advanced/s3.md | 68 +- pages/guide/advanced/search.md | 325 +- pages/guide/advanced/sso.md | 225 +- pages/guide/advanced/sso/01-GitHub.md | 54 +- pages/guide/advanced/sso/02-Dingtalk.md | 39 +- pages/guide/advanced/sso/03-Microsoft.md | 46 +- pages/guide/advanced/sso/04-Google.md | 58 +- pages/guide/advanced/sso/05-Casdoor.md | 48 +- pages/guide/advanced/sso/06-Authentik.md | 68 +- pages/guide/advanced/sso/07-keycloak.md | 168 +- pages/guide/advanced/strm.md | 5 +- pages/guide/advanced/user.md | 111 +- pages/guide/advanced/webdav.md | 201 +- pages/guide/drivers/115.md | 329 +- pages/guide/drivers/115_open.md | 243 +- pages/guide/drivers/123.md | 241 +- pages/guide/drivers/123_open.md | 181 +- pages/guide/drivers/139.md | 356 +- pages/guide/drivers/163music.md | 84 +- pages/guide/drivers/189.md | 176 +- pages/guide/drivers/alias.md | 16 +- pages/guide/drivers/alias_text.md | 385 +- pages/guide/drivers/alidoc.md | 113 +- pages/guide/drivers/aliyundrive_open.md | 451 +- pages/guide/drivers/autoindex.md | 142 +- pages/guide/drivers/baidu.md | 375 +- pages/guide/drivers/baidu_photo.md | 97 +- pages/guide/drivers/chaoxing.md | 87 +- pages/guide/drivers/chunk.md | 145 +- pages/guide/drivers/cloudflare-imgbed.md | 138 +- pages/guide/drivers/cloudreve.md | 119 +- pages/guide/drivers/cloudreve_v4.md | 209 +- pages/guide/drivers/cnb_releases.md | 89 +- pages/guide/drivers/common.md | 337 +- pages/guide/drivers/crypt.md | 339 +- pages/guide/drivers/degoo.md | 82 +- pages/guide/drivers/develop.md | 23 +- pages/guide/drivers/doubao.md | 99 +- pages/guide/drivers/dropbox.md | 165 +- pages/guide/drivers/emby.md | 102 +- pages/guide/drivers/febbox.md | 100 +- pages/guide/drivers/feiji.md | 81 +- pages/guide/drivers/ftp.md | 89 +- pages/guide/drivers/github.md | 203 +- pages/guide/drivers/github_releases.md | 270 +- pages/guide/drivers/google_drive.md | 274 +- pages/guide/drivers/google_photos.md | 125 +- pages/guide/drivers/guangyapan.md | 183 +- pages/guide/drivers/halalcloud.md | 104 +- pages/guide/drivers/ilanzou.md | 75 +- pages/guide/drivers/kodbox.md | 99 +- pages/guide/drivers/lanzou.md | 162 +- pages/guide/drivers/lenovo_nas_share.md | 103 +- pages/guide/drivers/local.md | 113 +- pages/guide/drivers/mediafire.md | 53 +- pages/guide/drivers/mediatrack.md | 82 +- pages/guide/drivers/mega.md | 117 +- pages/guide/drivers/misskey.md | 34 +- pages/guide/drivers/mopan.md | 94 +- pages/guide/drivers/onedrive.md | 216 +- pages/guide/drivers/onedrive_app.md | 234 +- pages/guide/drivers/openlist.md | 165 +- pages/guide/drivers/pikpak.md | 279 +- pages/guide/drivers/quark.md | 229 +- pages/guide/drivers/s3.md | 566 +- pages/guide/drivers/seafile.md | 140 +- pages/guide/drivers/sftp.md | 17 +- pages/guide/drivers/smb.md | 53 +- pages/guide/drivers/strm.md | 16 +- pages/guide/drivers/strm_text.md | 181 +- pages/guide/drivers/teambition.md | 72 +- pages/guide/drivers/teldrive.md | 124 +- pages/guide/drivers/terabox.md | 106 +- pages/guide/drivers/thunder.md | 820 +- pages/guide/drivers/uc.md | 145 +- pages/guide/drivers/url_tree.md | 17 +- pages/guide/drivers/uss.md | 137 +- pages/guide/drivers/webdav.md | 149 +- pages/guide/drivers/weiyun.md | 109 +- pages/guide/drivers/wopan.md | 134 +- pages/guide/drivers/wps.md | 70 +- pages/guide/drivers/yandex.md | 63 +- pages/guide/index.md | 106 +- pages/guide/installation/1panel.md | 104 +- pages/guide/installation/desktop.md | 419 +- pages/guide/installation/docker.md | 315 +- pages/guide/installation/download.md | 9 +- pages/guide/installation/koyeb.md | 214 +- pages/guide/installation/manual.md | 401 +- pages/guide/installation/paas.md | 98 +- pages/guide/installation/reverse-proxy.md | 173 +- pages/guide/installation/script.md | 60 +- pages/guide/installation/source.md | 75 +- pages/guide/installation/truenas.md | 98 +- pages/guide/installation/worker.md | 398 +- pages/guide/migrate.md | 152 +- pages/index.md | 34 +- pages/privacy.md | 43 +- {public => pages/public}/favicon.svg | 0 .../public}/img/1panel/container.png | Bin .../public}/img/1panel/install.png | Bin .../public}/img/1panel/installed.png | Bin .../public}/img/1panel/search.png | Bin .../public}/img/1panel/terminal.png | Bin .../public}/img/advanced/TrollStore.jpg | Bin .../public}/img/advanced/backup.png | Bin .../public}/img/advanced/balance.png | Bin .../public}/img/advanced/casdoor-user.png | Bin .../public}/img/advanced/casdoor.png | Bin .../public}/img/advanced/dingding.png | Bin .../public}/img/advanced/github-login.png | Bin .../public}/img/advanced/github.png | Bin .../public}/img/advanced/google.png | Bin .../public}/img/advanced/hide-tips.png | Bin .../public}/img/advanced/home.png | Bin .../img/advanced/index_settings_panel.png | Bin .../public}/img/advanced/meta/password_b.png | Bin .../public}/img/advanced/meta/password_h.png | Bin .../public}/img/advanced/offline-download.png | Bin .../public}/img/advanced/qbittorrent.png | Bin .../public}/img/advanced/r_upload_1.png | Bin .../public}/img/advanced/r_upload_2.png | Bin .../public}/img/advanced/s3/s3_add_backup.png | Bin .../public}/img/advanced/s3/s3_config.png | Bin .../public}/img/advanced/s3/s3browser.png | Bin .../public}/img/advanced/s3/s3drive.png | Bin .../public}/img/advanced/sqlite-mysql.png | Bin .../public}/img/advanced/sqlite3.png | Bin .../public}/img/advanced/sso-add.png | Bin .../public}/img/advanced/sso-dir.png | Bin .../public}/img/advanced/sso-group.png | Bin .../public}/img/advanced/sso-org-config.png | Bin .../public}/img/advanced/sso-permission_b.png | Bin .../public}/img/advanced/sso-permission_h.png | Bin .../public}/img/advanced/sso/keycloak-01.png | Bin .../public}/img/advanced/sso/keycloak-02.png | Bin .../public}/img/advanced/sso/keycloak-03.png | Bin .../public}/img/advanced/sso_add_app.png | Bin .../public}/img/advanced/sso_cm.png | Bin .../public}/img/advanced/sso_fix_logo.png | Bin .../public}/img/advanced/user_manage.png | Bin .../img/advanced/user_read_archives_dark.png | Bin .../img/advanced/user_read_archives_light.png | Bin .../public}/img/advanced/video.png | Bin .../public}/img/advanced/weiruan.png | Bin .../public}/img/api/apifox-dark.png | Bin .../public}/img/api/apifox-light.png | Bin .../public}/img/config/open-with.png | Bin .../public}/img/config/readme_b.png | Bin .../public}/img/config/readme_h.png | Bin .../config/side/offline-download-modal-en.png | Bin .../config/side/offline-download-modal-zh.png | Bin .../img/config/side/upload-modal-en.png | Bin .../img/config/side/upload-modal-zh.png | Bin .../public}/img/desktop/dashboard.png | Bin {public => pages/public}/img/desktop/logs.png | Bin .../public}/img/desktop/mount.png | Bin .../public}/img/drivers/115/115-add_error.png | Bin .../public}/img/drivers/115/115-file_cid.png | Bin .../public}/img/drivers/115/115-open-01-d.png | Bin .../public}/img/drivers/115/115-open-01-l.png | Bin .../img/drivers/115/115-open-02-01-d.png | Bin .../img/drivers/115/115-open-02-01-l.png | Bin .../img/drivers/115/115-open-02-02-d.png | Bin .../img/drivers/115/115-open-02-02-l.png | Bin .../public}/img/drivers/115/115-open-03.png | Bin .../public}/img/drivers/115/115-open-04-d.png | Bin .../public}/img/drivers/115/115-open-04-l.png | Bin .../public}/img/drivers/115/115-open-05-d.png | Bin .../public}/img/drivers/115/115-open-05-l.png | Bin .../img/drivers/115/115-open-application.jpg | Bin .../public}/img/drivers/115/115-root_cid.png | Bin .../public}/img/drivers/115/115-url_demo.png | Bin .../public}/img/drivers/123/123.png | Bin .../public}/img/drivers/123/123_add_demo.png | Bin .../public}/img/drivers/123/123_fl_id.png | Bin .../public}/img/drivers/123/123_link_1.png | Bin .../public}/img/drivers/123/123_link_2.png | Bin .../public}/img/drivers/123/123_link_3.png | Bin .../public}/img/drivers/123/123_link_4.png | Bin .../public}/img/drivers/123/123open-02.png | Bin .../public}/img/drivers/123/123open-03.png | Bin .../public}/img/drivers/139/139_new_au.png | Bin .../public}/img/drivers/139/add-family.png | Bin .../public}/img/drivers/139/add-personal.png | Bin .../img/drivers/139/add_new_personal.png | Bin .../public}/img/drivers/139/ch-family.png | Bin .../public}/img/drivers/139/ch-personal.png | Bin .../public}/img/drivers/139/new_personal.png | Bin .../public}/img/drivers/139/other-family.png | Bin .../img/drivers/139/other-personal.png | Bin .../public}/img/drivers/163/163_cookie.png | Bin .../public}/img/drivers/189/189-0.png | Bin .../public}/img/drivers/189/189-1.png | Bin .../public}/img/drivers/189/189-2.png | Bin .../public}/img/drivers/189/189.png | Bin .../public}/img/drivers/189/3.png | Bin .../public}/img/drivers/189/4.png | Bin .../OneDrive/onedrive-new-password.png | Bin .../OneDrive/onedrive-register-app.png | Bin .../OneDrive/onedrive-update-permission.png | Bin .../img/drivers/OneDrive/online_api.png | Bin .../drivers/OneDrive/online_api_config.png | Bin .../public}/img/drivers/alias/alias-2.png | Bin .../public}/img/drivers/alias/alias-3.png | Bin .../public}/img/drivers/alias/alias.png | Bin .../img/drivers/alias/read_policy_all.png | Bin .../public}/img/drivers/alist/alist.png | Bin .../img/drivers/aliyun/aliyun-open-01-d.png | Bin .../img/drivers/aliyun/aliyun-open-01-l.png | Bin .../drivers/aliyun/aliyun-open-02-01-d.png | Bin .../drivers/aliyun/aliyun-open-02-01-l.png | Bin .../drivers/aliyun/aliyun-open-02-02-d.png | Bin .../drivers/aliyun/aliyun-open-02-02-l.png | Bin .../img/drivers/aliyun/aliyun-open-03.png | Bin .../img/drivers/aliyun/aliyun-open-04-d.png | Bin .../img/drivers/aliyun/aliyun-open-04-l.png | Bin .../img/drivers/aliyun/aliyun-open-05-d.png | Bin .../img/drivers/aliyun/aliyun-open-05-l.png | Bin .../img/drivers/aliyun/aliyun-open-06-d.png | Bin .../img/drivers/aliyun/aliyun-open-06-l.png | Bin .../img/drivers/aliyun/aliyun-open-07-d.png | Bin .../img/drivers/aliyun/aliyun-open-07-l.png | Bin .../public}/img/drivers/aliyun/aliyun_api.png | Bin .../img/drivers/aliyun/aliyun_type.png | Bin .../public}/img/drivers/aliyundrive.png | Bin .../public}/img/drivers/baidu/BDUSS.png | Bin .../public}/img/drivers/baidu/aria2-ua.png | Bin .../public}/img/drivers/baidu/bdUA.png | Bin .../img/drivers/baidu/crack_dev_token.png | Bin .../img/drivers/baidu/dev_app_console.png | Bin .../drivers/baidu/dev_app_create_button.png | Bin .../drivers/baidu/dev_app_create_panel.png | Bin .../img/drivers/baidu/dev_app_info.png | Bin .../drivers/baidu/dev_app_settings_safety.png | Bin .../img/drivers/baidu/dev_platform.png | Bin .../drivers/baidu/dev_platform_console.png | Bin .../img/drivers/baidu/dev_platform_login.png | Bin .../public}/img/drivers/baidu/dev_token.png | Bin .../public}/img/drivers/baidu/idm-ua.png | Bin .../public}/img/drivers/baidu/motrix-ua.png | Bin .../img/drivers/baidu/non_dev_token.png | Bin .../public}/img/drivers/baidu/openapi.png | Bin .../public}/img/drivers/baidu/yike_cookie.png | Bin .../img/drivers/chaoxing/chaoxing_bbsid.png | Bin .../drivers/chaoxing/chaoxing_folder_id.gif | Bin .../chunk/chunk_large_only_disabled.png | Bin .../chunk/chunk_large_only_enabled.png | Bin .../img/drivers/chunk/custom_ext_disabled.png | Bin .../img/drivers/chunk/custom_ext_enabled.png | Bin .../public}/img/drivers/chunk/example.png | Bin .../cloudflare-imgbed/cloudflare-imgbed-1.png | Bin .../cloudreve/cloudreve_get_cookie.png | Bin .../cloudreve_v4_token_local_storage.png | Bin .../cloudreve_v4/cloudreve_v4_token_resp.png | Bin .../public}/img/drivers/common/path_b.png | Bin .../public}/img/drivers/common/path_h.png | Bin .../public}/img/drivers/crypt/crypt-demo1.png | Bin .../public}/img/drivers/crypt/crypt-demo2.png | Bin .../img/drivers/doubao/doubao-cookie.png | Bin .../drivers/doubao/doubao-root-folder-id.png | Bin .../public}/img/drivers/dropbox/1.png | Bin .../public}/img/drivers/dropbox/2.png | Bin .../public}/img/drivers/dropbox/3.png | Bin .../public}/img/drivers/dropbox/4.png | Bin .../public}/img/drivers/dropbox/5.png | Bin .../public}/img/drivers/dropbox/6.png | Bin .../public}/img/drivers/dropbox/7.png | Bin .../public}/img/drivers/dropbox/folder_id.png | Bin .../public}/img/drivers/febbox/febox.png | Bin .../public}/img/drivers/feiji/feiji.png | Bin .../public}/img/drivers/github/1.png | Bin .../public}/img/drivers/github/2.png | Bin .../public}/img/drivers/github/3.png | Bin .../img/drivers/google/google-00-d.png | Bin .../img/drivers/google/google-00-l.png | Bin .../img/drivers/google/google-01-01-d.png | Bin .../img/drivers/google/google-01-01-l.png | Bin .../img/drivers/google/google-01-02-d.png | Bin .../img/drivers/google/google-01-02-l.png | Bin .../public}/img/drivers/google/google-02.png | Bin .../public}/img/drivers/google/google-03.png | Bin .../public}/img/drivers/google/google-04.png | Bin .../img/drivers/google/google-05-d.png | Bin .../img/drivers/google/google-05-l.png | Bin .../img/drivers/google/google-06-d.png | Bin .../img/drivers/google/google-06-l.png | Bin .../img/drivers/google/google-07-d.png | Bin .../img/drivers/google/google-07-l.png | Bin .../img/drivers/google/google-08-d.png | Bin .../img/drivers/google/google-08-l.png | Bin .../img/drivers/google/google-oauth-00.png | Bin .../img/drivers/google/google-oauth-01.png | Bin .../img/drivers/google/google-oauth-02.png | Bin .../img/drivers/google/google-oauth-03.png | Bin .../drivers/google/google-oauth-eng-00.png | Bin .../drivers/google/google-oauth-eng-01.png | Bin .../drivers/google/google-oauth-eng-02.png | Bin .../drivers/google/google-oauth-eng-03.png | Bin .../img/drivers/google/googledrive-dir.png | Bin .../halalcloud_add_authorization.png | Bin .../kodbox/kodbox_root_folder_path_en.jpg | Bin .../kodbox/kodbox_root_folder_path_zh.jpg | Bin .../img/drivers/lanzou/ilanzou_folder.png | Bin .../public}/img/drivers/lanzou/lanzou1.png | Bin .../public}/img/drivers/lanzou/lanzou2.gif | Bin .../public}/img/drivers/lanzou/lanzou3.png | Bin .../public}/img/drivers/lanzou/lanzou_add.png | Bin .../lenovonasshare/lenovonasshare_fileid.png | Bin .../drivers/mediafire/mediafire-cookie.png | Bin .../mediafire/mediafire-sessiontoken.png | Bin .../img/drivers/mediatrack-projectid.png | Bin .../public}/img/drivers/mediatrack-rootid.png | Bin .../public}/img/drivers/mediatrack-token.png | Bin .../public}/img/drivers/mega/mega_2fa.png | Bin .../img/drivers/mega/mega_2fa_code.png | Bin .../public}/img/drivers/mega/mega_add.png | Bin .../public}/img/drivers/mopan/add-mopan.png | Bin .../img/drivers/offline_download_error.png | Bin .../img/drivers/onedrive_app/od_app_ok.png | Bin .../img/drivers/onedrive_app/od_share_add.png | Bin .../drivers/onedrive_app/onedrive_app0.png | Bin .../drivers/onedrive_app/onedrive_app1.png | Bin .../drivers/onedrive_app/onedrive_app3.png | Bin .../drivers/onedrive_app/onedrive_app4.png | Bin .../drivers/onedrive_app/onedrive_app5.png | Bin .../drivers/onedrive_app/onedrive_app6.png | Bin .../drivers/onedrive_app/onedrive_app7.png | Bin .../onedrive_app/onedrive_app_Denmo.png | Bin .../onedrive_app/onedrive_app_user.png | Bin .../public}/img/drivers/pikpak.png | Bin .../img/drivers/pikpak/pikpak-error-1.png | Bin .../img/drivers/pikpak/pikpak-error-2.png | Bin .../pikpak/pikpak-share-rootfolder.png | Bin .../img/drivers/pikpak/pikpak-share.png | Bin .../img/drivers/pikpak/pikpak-token-1.png | Bin .../img/drivers/pikpak/pikpak-token-2.png | Bin .../public}/img/drivers/quark-rootid.png | Bin .../public}/img/drivers/quark.png | Bin .../img/drivers/quark/quark_cookie.png | Bin .../img/drivers/quark/quark_fileid.png | Bin .../public}/img/drivers/ref_token.png | Bin .../public}/img/drivers/s3/139.png | Bin .../public}/img/drivers/s3/al.png | Bin .../public}/img/drivers/s3/aws.png | Bin .../public}/img/drivers/s3/b2.png | Bin .../public}/img/drivers/s3/bd.png | Bin .../public}/img/drivers/s3/cfr2.png | Bin .../public}/img/drivers/s3/doge.png | Bin .../public}/img/drivers/s3/googleStorage1.png | Bin .../public}/img/drivers/s3/googleStorage2.png | Bin .../public}/img/drivers/s3/googleStorage3.png | Bin .../public}/img/drivers/s3/googleStorage4.png | Bin .../public}/img/drivers/s3/hw.png | Bin .../public}/img/drivers/s3/jd.png | Bin .../public}/img/drivers/s3/mini-io.png | Bin .../public}/img/drivers/s3/qn.png | Bin .../public}/img/drivers/s3/sc.png | Bin .../public}/img/drivers/s3/tx.png | Bin .../public}/img/drivers/s3/ty.png | Bin .../public}/img/drivers/s3/up.png | Bin .../public}/img/drivers/s3/v1andv2.png | Bin .../public}/img/drivers/s3/zijie_tos.png | Bin .../public}/img/drivers/teambition-cookie.png | Bin .../public}/img/drivers/teambition-id.png | Bin .../teldrive/fetch_teldrive_cookie.png | Bin .../public}/img/drivers/terabox/terabox1.png | Bin .../public}/img/drivers/terabox/terabox2.png | Bin .../public}/img/drivers/terabox/terabox3.png | Bin .../img/drivers/terabox/terabox_cookie.png | Bin .../img/drivers/terabox/terabox_f12.png | Bin .../img/drivers/terabox/terabox_js_token.png | Bin .../public}/img/drivers/tv_qrcode.png | Bin .../public}/img/drivers/uc/uc_cookie.png | Bin .../public}/img/drivers/uc/uc_fileid.png | Bin .../img/drivers/vtencent/vtencent_close.png | Bin .../img/drivers/vtencent/vtencent_cookie.png | Bin .../img/drivers/webdav/webdav_Security.png | Bin .../img/drivers/weiyun/weiyun_cookie.png | Bin .../img/drivers/weiyun/weiyun_dir_id.png | Bin .../img/drivers/weiyun/weiyun_fl_id.png | Bin .../public}/img/drivers/wopan/add-wopan.png | Bin .../public}/img/drivers/wopan/wopan-req.png | Bin .../public}/img/drivers/wopan/wopan-resp.png | Bin .../drivers/wopan/wopan-session-storage.png | Bin .../public}/img/drivers/xunlei/SMS.png | Bin .../public}/img/drivers/xunlei/input.png | Bin .../public}/img/drivers/xunlei/output.png | Bin .../img/drivers/xunlei/storage_error.png | Bin .../public}/img/drivers/xunlei/x3.png | Bin .../public}/img/drivers/xunlei/x4.png | Bin .../public}/img/drivers/xunlei/x5.png | Bin .../public}/img/drivers/xunlei/x6.png | Bin .../public}/img/drivers/xunlei/x_br_add1.png | Bin .../public}/img/drivers/xunlei/x_br_add2.png | Bin .../public}/img/drivers/xunlei/x_br_error.png | Bin .../public}/img/drivers/xunlei/x_br_foled.png | Bin .../public}/img/drivers/xunlei/xlx_name.jpg | Bin .../public}/img/drivers/xunlei/xlx_z1.jpg | Bin .../public}/img/drivers/xunlei/xlx_z2.jpg | Bin {public => pages/public}/img/faq/123error.png | Bin {public => pages/public}/img/faq/add.jpeg | Bin .../public}/img/faq/alist-admin.gif | Bin .../public}/img/faq/alist-admin.png | Bin {public => pages/public}/img/faq/download.png | Bin {public => pages/public}/img/faq/error.png | Bin .../img/guide/installation/clawcloud-01.png | Bin .../img/guide/installation/clawcloud-run.svg | 0 .../img/guide/migrate/aliyun_remove1.png | Bin .../img/guide/migrate/aliyun_remove2.png | Bin .../img/guide/migrate/aliyun_remove3.png | Bin .../img/guide/migrate/aliyun_remove4.jpg | Bin .../img/guide/migrate/baidu_remove1.png | Bin .../img/guide/migrate/baidu_remove2.png | Bin .../img/guide/migrate/baidu_remove3.jpg | Bin .../img/guide/migrate/google_remove1.png | Bin .../img/guide/migrate/google_remove2.png | Bin .../img/guide/migrate/google_remove3.png | Bin .../public}/img/guide/migrate/odb_remove1.jpg | Bin .../public}/img/guide/migrate/odp_remove1.jpg | Bin .../bt_delete_default_config_01.png | Bin .../bt_delete_default_config_02.png | Bin .../guide/reverse_proxy/bt_new_website.png | Bin .../guide/reverse_proxy/bt_new_website_01.png | Bin .../guide/reverse_proxy/bt_reverse_proxy.png | Bin .../public}/img/guide/webdav/es-android.png | Bin .../public}/img/guide/webdav/es-ios.png | Bin .../public}/img/guide/webdav/fileball.png | Bin .../public}/img/guide/webdav/infuse.png | Bin .../public}/img/guide/webdav/nas.png | Bin .../public}/img/guide/webdav/nplayer.png | Bin .../public}/img/guide/webdav/potplayer.png | Bin .../public}/img/guide/webdav/reex.png | Bin .../public}/img/koyeb/db_host.png | Bin .../public}/img/koyeb/deployment.png | Bin .../public}/img/koyeb/environment.png | Bin {public => pages/public}/img/koyeb/file.png | Bin .../public}/img/koyeb/success.png | Bin .../InstallCustomAppApplicationName.png | Bin .../InstallCustomAppContainerEntrypoint.png | Bin .../InstallCustomAppImageConfiguration.png | Bin .../InstallCustomAppNetworkConfiguration.png | Bin .../InstallCustomAppPortalConfiguration.png | Bin ...lCustomAppSecurityContextConfiguration.png | Bin .../InstallCustomAppStorageConfiguration.png | Bin .../InstallCustomResourcesConfiguration.png | Bin .../public}/img/worker/create_app1.png | Bin .../public}/img/worker/create_app2.png | Bin .../public}/img/worker/create_app3.png | Bin .../public}/img/worker/create_app4.png | Bin .../public}/img/worker/create_app5.png | Bin .../public}/img/worker/create_app6.png | Bin .../public}/img/worker/edgeone_ui1.png | Bin .../public}/img/worker/edgeone_ui2.png | Bin .../public}/img/worker/edgeone_ui3.png | Bin {assets => pages/public}/logo.svg | 0 {assets => pages/public}/logo.webp | Bin pages/seeds/background.md | 75 +- pages/seeds/design.md | 347 +- pages/seeds/faq.md | 77 +- pages/seeds/generate.md | 155 +- pages/seeds/index.md | 36 +- pages/seeds/rapid-upload.md | 147 +- pages/seeds/usage.md | 198 +- pages/snippets/reverse-tip.md | 5 + pages/snippets/tos-tip.md | 5 + pages/terms.md | 72 +- patches/README.md | 15 - patches/valaxy-theme-press@0.25.9.patch | 21 - patches/valaxy@0.25.9.patch | 16 - pnpm-lock.yaml | 8913 ----------------- pnpm-workspace.yaml | 8 - public/algolia-verify.html | 8 - public/img/dev-tip/correctcontent-dark.png | Bin 3470 -> 0 bytes public/img/dev-tip/correctcontent-light.png | Bin 3600 -> 0 bytes public/img/dev-tip/correctendline-dark.png | Bin 32076 -> 0 bytes public/img/dev-tip/correctendline-light.png | Bin 32661 -> 0 bytes public/img/dev-tip/correcttitle-dark.png | Bin 4748 -> 0 bytes public/img/dev-tip/correcttitle-light.png | Bin 4865 -> 0 bytes .../dev-tip/correcttranslateendline-dark.png | Bin 167244 -> 0 bytes .../dev-tip/correcttranslateendline-light.png | Bin 168668 -> 0 bytes public/img/dev-tip/wrongcontent-dark.png | Bin 3613 -> 0 bytes public/img/dev-tip/wrongcontent-light.png | Bin 3744 -> 0 bytes public/img/dev-tip/wrongendline-dark.png | Bin 31513 -> 0 bytes public/img/dev-tip/wrongendline-light.png | Bin 32119 -> 0 bytes public/img/dev-tip/wrongtitle-dark.png | Bin 8914 -> 0 bytes public/img/dev-tip/wrongtitle-light.png | Bin 9010 -> 0 bytes .../dev-tip/wrongtranslateendline-dark.png | Bin 166801 -> 0 bytes .../dev-tip/wrongtranslateendline-light.png | Bin 168209 -> 0 bytes public/logo.svg | 27 - public/logo.webp | Bin 61596 -> 0 bytes snippets/snippet-with-region.js | 7 - snippets/snippet.js | 3 - tsconfig.json | 16 +- types/image.d.ts | 3 - utils/ensure-full-git.js | 2 +- utils/languageDetector.ts | 136 - valaxy.config.ts | 201 - 742 files changed, 19673 insertions(+), 32274 deletions(-) delete mode 100644 .npmrc create mode 100644 .vitepress/config.mts create mode 100644 .vitepress/contributors.ts create mode 100644 .vitepress/plugins/mermaid.ts rename utils/markdown-theme-image.ts => .vitepress/plugins/theme-image.ts (91%) create mode 100644 .vitepress/sidebar.ts create mode 100644 .vitepress/theme/Layout.vue rename {components => .vitepress/theme/components}/BackToTop.vue (76%) rename {components => .vitepress/theme/components}/BiliBili.vue (98%) create mode 100644 .vitepress/theme/components/Contributors.vue create mode 100644 .vitepress/theme/components/Mermaid.vue rename {components => .vitepress/theme/components}/OpenListDownload.vue (99%) create mode 100644 .vitepress/theme/components/WorkInProgress.vue rename {components => .vitepress/theme/components}/openlist_releases_db.json (100%) create mode 100644 .vitepress/theme/i18n.ts create mode 100644 .vitepress/theme/index.ts create mode 100644 .vitepress/theme/language.ts rename styles/index.scss => .vitepress/theme/style.css (58%) rename {utils => .vitepress}/version.ts (100%) delete mode 100644 assets/sites.ts create mode 100644 bun.lock delete mode 100644 components/AddonGallery.vue delete mode 100644 components/AddonGalleryCard.vue delete mode 100644 components/AppLink.vue delete mode 100644 components/AutoLanguageDetector.vue delete mode 100644 components/BrandIcon.vue delete mode 100644 components/CustomVueDemo.vue delete mode 100644 components/InlineIcon.vue delete mode 100644 components/OpenListContributors.vue delete mode 100644 components/OpenListLogo.vue delete mode 100644 components/PressButton.vue delete mode 100644 components/PressGetStarted.vue delete mode 100644 components/PressHome.vue delete mode 100644 components/PressHomeHero.vue delete mode 100644 components/PressNavBar.vue delete mode 100644 components/PressNavScreenMenuLink.vue delete mode 100644 components/ThemeImage.vue delete mode 100644 components/ThemeImagePair.vue delete mode 100644 components/WorkInProgress.vue delete mode 100644 components/api/ApiDocsRedirect.vue delete mode 100644 layouts/default.vue delete mode 100644 layouts/home.vue delete mode 100644 layouts/statement.vue delete mode 100644 locales/en.yml delete mode 100644 locales/zh-CN.yml create mode 100644 pages/en/api/apidocs.md create mode 100644 pages/en/api/index.md create mode 100644 pages/en/configuration/configuration.md create mode 100644 pages/en/configuration/global.md create mode 100644 pages/en/configuration/index.md create mode 100644 pages/en/configuration/other.md create mode 100644 pages/en/configuration/preview.md create mode 100644 pages/en/configuration/side.md create mode 100644 pages/en/configuration/site.md create mode 100644 pages/en/configuration/style.md create mode 100644 pages/en/ecosystem/index.md create mode 100644 pages/en/ecosystem/official_APIpage.md create mode 100644 pages/en/ecosystem/official_desktop.md create mode 100644 pages/en/ecosystem/official_docs.md create mode 100644 pages/en/ecosystem/official_mobile.md create mode 100644 pages/en/ecosystem/official_proxy.md create mode 100644 pages/en/ecosystem/official_worker/about.md create mode 100644 pages/en/ecosystem/official_worker/basic.md create mode 100644 pages/en/ecosystem/official_worker/guide.md create mode 100644 pages/en/ecosystem/official_worker/guide_cfw.md create mode 100644 pages/en/ecosystem/official_worker/guide_env.md create mode 100644 pages/en/ecosystem/official_worker/guide_eom.md create mode 100644 pages/en/ecosystem/official_worker/guide_esa.md create mode 100644 pages/en/ecosystem/official_worker/index.md create mode 100644 pages/en/faq/_example.md create mode 100644 pages/en/faq/error.md create mode 100644 pages/en/faq/howto.md create mode 100644 pages/en/faq/index.md create mode 100644 pages/en/faq/seealso.md create mode 100644 pages/en/faq/why.md create mode 100644 pages/en/guide/advanced/2fa.md create mode 100644 pages/en/guide/advanced/alias.md create mode 100644 pages/en/guide/advanced/backup.md create mode 100644 pages/en/guide/advanced/balance.md create mode 100644 pages/en/guide/advanced/ftp.md create mode 100644 pages/en/guide/advanced/index.md create mode 100644 pages/en/guide/advanced/ipa.md create mode 100644 pages/en/guide/advanced/mcp.md create mode 100644 pages/en/guide/advanced/meta.md create mode 100644 pages/en/guide/advanced/offline-download.md create mode 100644 pages/en/guide/advanced/rapid_upload.md create mode 100644 pages/en/guide/advanced/s3.md create mode 100644 pages/en/guide/advanced/search.md create mode 100644 pages/en/guide/advanced/sso.md create mode 100644 pages/en/guide/advanced/sso/01-GitHub.md create mode 100644 pages/en/guide/advanced/sso/02-Dingtalk.md create mode 100644 pages/en/guide/advanced/sso/03-Microsoft.md create mode 100644 pages/en/guide/advanced/sso/04-Google.md create mode 100644 pages/en/guide/advanced/sso/05-Casdoor.md create mode 100644 pages/en/guide/advanced/sso/06-Authentik.md create mode 100644 pages/en/guide/advanced/sso/07-keycloak.md create mode 100644 pages/en/guide/advanced/strm.md create mode 100644 pages/en/guide/advanced/user.md create mode 100644 pages/en/guide/advanced/webdav.md create mode 100644 pages/en/guide/drivers/115.md create mode 100644 pages/en/guide/drivers/115_open.md create mode 100644 pages/en/guide/drivers/123.md create mode 100644 pages/en/guide/drivers/123_open.md create mode 100644 pages/en/guide/drivers/139.md create mode 100644 pages/en/guide/drivers/163music.md create mode 100644 pages/en/guide/drivers/189.md create mode 100644 pages/en/guide/drivers/alias.md create mode 100644 pages/en/guide/drivers/alias_text.md create mode 100644 pages/en/guide/drivers/alidoc.md create mode 100644 pages/en/guide/drivers/aliyundrive_open.md create mode 100644 pages/en/guide/drivers/autoindex.md create mode 100644 pages/en/guide/drivers/baidu.md create mode 100644 pages/en/guide/drivers/baidu_photo.md create mode 100644 pages/en/guide/drivers/chaoxing.md create mode 100644 pages/en/guide/drivers/chunk.md create mode 100644 pages/en/guide/drivers/cloudflare-imgbed.md create mode 100644 pages/en/guide/drivers/cloudreve.md create mode 100644 pages/en/guide/drivers/cloudreve_v4.md create mode 100644 pages/en/guide/drivers/cnb_releases.md create mode 100644 pages/en/guide/drivers/common.md create mode 100644 pages/en/guide/drivers/crypt.md create mode 100644 pages/en/guide/drivers/degoo.md create mode 100644 pages/en/guide/drivers/develop.md create mode 100644 pages/en/guide/drivers/doubao.md create mode 100644 pages/en/guide/drivers/dropbox.md create mode 100644 pages/en/guide/drivers/emby.md create mode 100644 pages/en/guide/drivers/febbox.md create mode 100644 pages/en/guide/drivers/feiji.md create mode 100644 pages/en/guide/drivers/ftp.md create mode 100644 pages/en/guide/drivers/github.md create mode 100644 pages/en/guide/drivers/github_releases.md create mode 100644 pages/en/guide/drivers/google_drive.md create mode 100644 pages/en/guide/drivers/google_photos.md create mode 100644 pages/en/guide/drivers/guangyapan.md create mode 100644 pages/en/guide/drivers/halalcloud.md create mode 100644 pages/en/guide/drivers/ilanzou.md create mode 100644 pages/en/guide/drivers/index.md create mode 100644 pages/en/guide/drivers/kodbox.md create mode 100644 pages/en/guide/drivers/lanzou.md create mode 100644 pages/en/guide/drivers/lenovo_nas_share.md create mode 100644 pages/en/guide/drivers/local.md create mode 100644 pages/en/guide/drivers/mediafire.md create mode 100644 pages/en/guide/drivers/mediatrack.md create mode 100644 pages/en/guide/drivers/mega.md create mode 100644 pages/en/guide/drivers/misskey.md create mode 100644 pages/en/guide/drivers/mopan.md create mode 100644 pages/en/guide/drivers/onedrive.md create mode 100644 pages/en/guide/drivers/onedrive_app.md create mode 100644 pages/en/guide/drivers/openlist.md create mode 100644 pages/en/guide/drivers/pikpak.md create mode 100644 pages/en/guide/drivers/quark.md create mode 100644 pages/en/guide/drivers/s3.md create mode 100644 pages/en/guide/drivers/seafile.md create mode 100644 pages/en/guide/drivers/sftp.md create mode 100644 pages/en/guide/drivers/smb.md create mode 100644 pages/en/guide/drivers/storage-list.md create mode 100644 pages/en/guide/drivers/strm.md create mode 100644 pages/en/guide/drivers/strm_text.md create mode 100644 pages/en/guide/drivers/teambition.md create mode 100644 pages/en/guide/drivers/teldrive.md create mode 100644 pages/en/guide/drivers/terabox.md create mode 100644 pages/en/guide/drivers/thunder.md create mode 100644 pages/en/guide/drivers/uc.md create mode 100644 pages/en/guide/drivers/url_tree.md create mode 100644 pages/en/guide/drivers/uss.md create mode 100644 pages/en/guide/drivers/webdav.md create mode 100644 pages/en/guide/drivers/weiyun.md create mode 100644 pages/en/guide/drivers/wopan.md create mode 100644 pages/en/guide/drivers/wps.md create mode 100644 pages/en/guide/drivers/yandex.md create mode 100644 pages/en/guide/index.md create mode 100644 pages/en/guide/installation/1panel.md create mode 100644 pages/en/guide/installation/desktop.md create mode 100644 pages/en/guide/installation/docker.md create mode 100644 pages/en/guide/installation/download.md create mode 100644 pages/en/guide/installation/index.md create mode 100644 pages/en/guide/installation/koyeb.md create mode 100644 pages/en/guide/installation/manual.md create mode 100644 pages/en/guide/installation/paas.md create mode 100644 pages/en/guide/installation/reverse-proxy.md create mode 100644 pages/en/guide/installation/script.md create mode 100644 pages/en/guide/installation/source.md create mode 100644 pages/en/guide/installation/truenas.md create mode 100644 pages/en/guide/installation/worker.md create mode 100644 pages/en/guide/migrate.md create mode 100644 pages/en/index.md create mode 100644 pages/en/privacy.md create mode 100644 pages/en/seeds/background.md create mode 100644 pages/en/seeds/design.md create mode 100644 pages/en/seeds/faq.md create mode 100644 pages/en/seeds/generate.md create mode 100644 pages/en/seeds/index.md create mode 100644 pages/en/seeds/rapid-upload.md create mode 100644 pages/en/seeds/usage.md rename {snippets => pages/en/snippets}/reverse-tip.md (54%) rename {snippets => pages/en/snippets}/tos-tip.md (58%) create mode 100644 pages/en/terms.md rename {public => pages/public}/favicon.svg (100%) rename {public => pages/public}/img/1panel/container.png (100%) rename {public => pages/public}/img/1panel/install.png (100%) rename {public => pages/public}/img/1panel/installed.png (100%) rename {public => pages/public}/img/1panel/search.png (100%) rename {public => pages/public}/img/1panel/terminal.png (100%) rename {public => pages/public}/img/advanced/TrollStore.jpg (100%) rename {public => pages/public}/img/advanced/backup.png (100%) rename {public => pages/public}/img/advanced/balance.png (100%) rename {public => pages/public}/img/advanced/casdoor-user.png (100%) rename {public => pages/public}/img/advanced/casdoor.png (100%) rename {public => pages/public}/img/advanced/dingding.png (100%) rename {public => pages/public}/img/advanced/github-login.png (100%) rename {public => pages/public}/img/advanced/github.png (100%) rename {public => pages/public}/img/advanced/google.png (100%) rename {public => pages/public}/img/advanced/hide-tips.png (100%) rename {public => pages/public}/img/advanced/home.png (100%) rename {public => pages/public}/img/advanced/index_settings_panel.png (100%) rename {public => pages/public}/img/advanced/meta/password_b.png (100%) rename {public => pages/public}/img/advanced/meta/password_h.png (100%) rename {public => pages/public}/img/advanced/offline-download.png (100%) rename {public => pages/public}/img/advanced/qbittorrent.png (100%) rename {public => pages/public}/img/advanced/r_upload_1.png (100%) rename {public => pages/public}/img/advanced/r_upload_2.png (100%) rename {public => pages/public}/img/advanced/s3/s3_add_backup.png (100%) rename {public => pages/public}/img/advanced/s3/s3_config.png (100%) rename {public => pages/public}/img/advanced/s3/s3browser.png (100%) rename {public => pages/public}/img/advanced/s3/s3drive.png (100%) rename {public => pages/public}/img/advanced/sqlite-mysql.png (100%) rename {public => pages/public}/img/advanced/sqlite3.png (100%) rename {public => pages/public}/img/advanced/sso-add.png (100%) rename {public => pages/public}/img/advanced/sso-dir.png (100%) rename {public => pages/public}/img/advanced/sso-group.png (100%) rename {public => pages/public}/img/advanced/sso-org-config.png (100%) rename {public => pages/public}/img/advanced/sso-permission_b.png (100%) rename {public => pages/public}/img/advanced/sso-permission_h.png (100%) rename {public => pages/public}/img/advanced/sso/keycloak-01.png (100%) rename {public => pages/public}/img/advanced/sso/keycloak-02.png (100%) rename {public => pages/public}/img/advanced/sso/keycloak-03.png (100%) rename {public => pages/public}/img/advanced/sso_add_app.png (100%) rename {public => pages/public}/img/advanced/sso_cm.png (100%) rename {public => pages/public}/img/advanced/sso_fix_logo.png (100%) rename {public => pages/public}/img/advanced/user_manage.png (100%) rename {public => pages/public}/img/advanced/user_read_archives_dark.png (100%) rename {public => pages/public}/img/advanced/user_read_archives_light.png (100%) rename {public => pages/public}/img/advanced/video.png (100%) rename {public => pages/public}/img/advanced/weiruan.png (100%) rename {public => pages/public}/img/api/apifox-dark.png (100%) rename {public => pages/public}/img/api/apifox-light.png (100%) rename {public => pages/public}/img/config/open-with.png (100%) rename {public => pages/public}/img/config/readme_b.png (100%) rename {public => pages/public}/img/config/readme_h.png (100%) rename {public => pages/public}/img/config/side/offline-download-modal-en.png (100%) rename {public => pages/public}/img/config/side/offline-download-modal-zh.png (100%) rename {public => pages/public}/img/config/side/upload-modal-en.png (100%) rename {public => pages/public}/img/config/side/upload-modal-zh.png (100%) rename {public => pages/public}/img/desktop/dashboard.png (100%) rename {public => pages/public}/img/desktop/logs.png (100%) rename {public => pages/public}/img/desktop/mount.png (100%) rename {public => pages/public}/img/drivers/115/115-add_error.png (100%) rename {public => pages/public}/img/drivers/115/115-file_cid.png (100%) rename {public => pages/public}/img/drivers/115/115-open-01-d.png (100%) rename {public => pages/public}/img/drivers/115/115-open-01-l.png (100%) rename {public => pages/public}/img/drivers/115/115-open-02-01-d.png (100%) rename {public => pages/public}/img/drivers/115/115-open-02-01-l.png (100%) rename {public => pages/public}/img/drivers/115/115-open-02-02-d.png (100%) rename {public => pages/public}/img/drivers/115/115-open-02-02-l.png (100%) rename {public => pages/public}/img/drivers/115/115-open-03.png (100%) rename {public => pages/public}/img/drivers/115/115-open-04-d.png (100%) rename {public => pages/public}/img/drivers/115/115-open-04-l.png (100%) rename {public => pages/public}/img/drivers/115/115-open-05-d.png (100%) rename {public => pages/public}/img/drivers/115/115-open-05-l.png (100%) rename {public => pages/public}/img/drivers/115/115-open-application.jpg (100%) rename {public => pages/public}/img/drivers/115/115-root_cid.png (100%) rename {public => pages/public}/img/drivers/115/115-url_demo.png (100%) rename {public => pages/public}/img/drivers/123/123.png (100%) rename {public => pages/public}/img/drivers/123/123_add_demo.png (100%) rename {public => pages/public}/img/drivers/123/123_fl_id.png (100%) rename {public => pages/public}/img/drivers/123/123_link_1.png (100%) rename {public => pages/public}/img/drivers/123/123_link_2.png (100%) rename {public => pages/public}/img/drivers/123/123_link_3.png (100%) rename {public => pages/public}/img/drivers/123/123_link_4.png (100%) rename {public => pages/public}/img/drivers/123/123open-02.png (100%) rename {public => pages/public}/img/drivers/123/123open-03.png (100%) rename {public => pages/public}/img/drivers/139/139_new_au.png (100%) rename {public => pages/public}/img/drivers/139/add-family.png (100%) rename {public => pages/public}/img/drivers/139/add-personal.png (100%) rename {public => pages/public}/img/drivers/139/add_new_personal.png (100%) rename {public => pages/public}/img/drivers/139/ch-family.png (100%) rename {public => pages/public}/img/drivers/139/ch-personal.png (100%) rename {public => pages/public}/img/drivers/139/new_personal.png (100%) rename {public => pages/public}/img/drivers/139/other-family.png (100%) rename {public => pages/public}/img/drivers/139/other-personal.png (100%) rename {public => pages/public}/img/drivers/163/163_cookie.png (100%) rename {public => pages/public}/img/drivers/189/189-0.png (100%) rename {public => pages/public}/img/drivers/189/189-1.png (100%) rename {public => pages/public}/img/drivers/189/189-2.png (100%) rename {public => pages/public}/img/drivers/189/189.png (100%) rename {public => pages/public}/img/drivers/189/3.png (100%) rename {public => pages/public}/img/drivers/189/4.png (100%) rename {public => pages/public}/img/drivers/OneDrive/onedrive-new-password.png (100%) rename {public => pages/public}/img/drivers/OneDrive/onedrive-register-app.png (100%) rename {public => pages/public}/img/drivers/OneDrive/onedrive-update-permission.png (100%) rename {public => pages/public}/img/drivers/OneDrive/online_api.png (100%) rename {public => pages/public}/img/drivers/OneDrive/online_api_config.png (100%) rename {public => pages/public}/img/drivers/alias/alias-2.png (100%) rename {public => pages/public}/img/drivers/alias/alias-3.png (100%) rename {public => pages/public}/img/drivers/alias/alias.png (100%) rename {public => pages/public}/img/drivers/alias/read_policy_all.png (100%) rename {public => pages/public}/img/drivers/alist/alist.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-01-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-01-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-02-01-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-02-01-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-02-02-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-02-02-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-03.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-04-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-04-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-05-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-05-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-06-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-06-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-07-d.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun-open-07-l.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun_api.png (100%) rename {public => pages/public}/img/drivers/aliyun/aliyun_type.png (100%) rename {public => pages/public}/img/drivers/aliyundrive.png (100%) rename {public => pages/public}/img/drivers/baidu/BDUSS.png (100%) rename {public => pages/public}/img/drivers/baidu/aria2-ua.png (100%) rename {public => pages/public}/img/drivers/baidu/bdUA.png (100%) rename {public => pages/public}/img/drivers/baidu/crack_dev_token.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_app_console.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_app_create_button.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_app_create_panel.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_app_info.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_app_settings_safety.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_platform.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_platform_console.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_platform_login.png (100%) rename {public => pages/public}/img/drivers/baidu/dev_token.png (100%) rename {public => pages/public}/img/drivers/baidu/idm-ua.png (100%) rename {public => pages/public}/img/drivers/baidu/motrix-ua.png (100%) rename {public => pages/public}/img/drivers/baidu/non_dev_token.png (100%) rename {public => pages/public}/img/drivers/baidu/openapi.png (100%) rename {public => pages/public}/img/drivers/baidu/yike_cookie.png (100%) rename {public => pages/public}/img/drivers/chaoxing/chaoxing_bbsid.png (100%) rename {public => pages/public}/img/drivers/chaoxing/chaoxing_folder_id.gif (100%) rename {public => pages/public}/img/drivers/chunk/chunk_large_only_disabled.png (100%) rename {public => pages/public}/img/drivers/chunk/chunk_large_only_enabled.png (100%) rename {public => pages/public}/img/drivers/chunk/custom_ext_disabled.png (100%) rename {public => pages/public}/img/drivers/chunk/custom_ext_enabled.png (100%) rename {public => pages/public}/img/drivers/chunk/example.png (100%) rename {public => pages/public}/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png (100%) rename {public => pages/public}/img/drivers/cloudreve/cloudreve_get_cookie.png (100%) rename {public => pages/public}/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png (100%) rename {public => pages/public}/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png (100%) rename {public => pages/public}/img/drivers/common/path_b.png (100%) rename {public => pages/public}/img/drivers/common/path_h.png (100%) rename {public => pages/public}/img/drivers/crypt/crypt-demo1.png (100%) rename {public => pages/public}/img/drivers/crypt/crypt-demo2.png (100%) rename {public => pages/public}/img/drivers/doubao/doubao-cookie.png (100%) rename {public => pages/public}/img/drivers/doubao/doubao-root-folder-id.png (100%) rename {public => pages/public}/img/drivers/dropbox/1.png (100%) rename {public => pages/public}/img/drivers/dropbox/2.png (100%) rename {public => pages/public}/img/drivers/dropbox/3.png (100%) rename {public => pages/public}/img/drivers/dropbox/4.png (100%) rename {public => pages/public}/img/drivers/dropbox/5.png (100%) rename {public => pages/public}/img/drivers/dropbox/6.png (100%) rename {public => pages/public}/img/drivers/dropbox/7.png (100%) rename {public => pages/public}/img/drivers/dropbox/folder_id.png (100%) rename {public => pages/public}/img/drivers/febbox/febox.png (100%) rename {public => pages/public}/img/drivers/feiji/feiji.png (100%) rename {public => pages/public}/img/drivers/github/1.png (100%) rename {public => pages/public}/img/drivers/github/2.png (100%) rename {public => pages/public}/img/drivers/github/3.png (100%) rename {public => pages/public}/img/drivers/google/google-00-d.png (100%) rename {public => pages/public}/img/drivers/google/google-00-l.png (100%) rename {public => pages/public}/img/drivers/google/google-01-01-d.png (100%) rename {public => pages/public}/img/drivers/google/google-01-01-l.png (100%) rename {public => pages/public}/img/drivers/google/google-01-02-d.png (100%) rename {public => pages/public}/img/drivers/google/google-01-02-l.png (100%) rename {public => pages/public}/img/drivers/google/google-02.png (100%) rename {public => pages/public}/img/drivers/google/google-03.png (100%) rename {public => pages/public}/img/drivers/google/google-04.png (100%) rename {public => pages/public}/img/drivers/google/google-05-d.png (100%) rename {public => pages/public}/img/drivers/google/google-05-l.png (100%) rename {public => pages/public}/img/drivers/google/google-06-d.png (100%) rename {public => pages/public}/img/drivers/google/google-06-l.png (100%) rename {public => pages/public}/img/drivers/google/google-07-d.png (100%) rename {public => pages/public}/img/drivers/google/google-07-l.png (100%) rename {public => pages/public}/img/drivers/google/google-08-d.png (100%) rename {public => pages/public}/img/drivers/google/google-08-l.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-00.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-01.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-02.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-03.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-eng-00.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-eng-01.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-eng-02.png (100%) rename {public => pages/public}/img/drivers/google/google-oauth-eng-03.png (100%) rename {public => pages/public}/img/drivers/google/googledrive-dir.png (100%) rename {public => pages/public}/img/drivers/halalcloud/halalcloud_add_authorization.png (100%) rename {public => pages/public}/img/drivers/kodbox/kodbox_root_folder_path_en.jpg (100%) rename {public => pages/public}/img/drivers/kodbox/kodbox_root_folder_path_zh.jpg (100%) rename {public => pages/public}/img/drivers/lanzou/ilanzou_folder.png (100%) rename {public => pages/public}/img/drivers/lanzou/lanzou1.png (100%) rename {public => pages/public}/img/drivers/lanzou/lanzou2.gif (100%) rename {public => pages/public}/img/drivers/lanzou/lanzou3.png (100%) rename {public => pages/public}/img/drivers/lanzou/lanzou_add.png (100%) rename {public => pages/public}/img/drivers/lenovonasshare/lenovonasshare_fileid.png (100%) rename {public => pages/public}/img/drivers/mediafire/mediafire-cookie.png (100%) rename {public => pages/public}/img/drivers/mediafire/mediafire-sessiontoken.png (100%) rename {public => pages/public}/img/drivers/mediatrack-projectid.png (100%) rename {public => pages/public}/img/drivers/mediatrack-rootid.png (100%) rename {public => pages/public}/img/drivers/mediatrack-token.png (100%) rename {public => pages/public}/img/drivers/mega/mega_2fa.png (100%) rename {public => pages/public}/img/drivers/mega/mega_2fa_code.png (100%) rename {public => pages/public}/img/drivers/mega/mega_add.png (100%) rename {public => pages/public}/img/drivers/mopan/add-mopan.png (100%) rename {public => pages/public}/img/drivers/offline_download_error.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/od_app_ok.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/od_share_add.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app0.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app1.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app3.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app4.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app5.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app6.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app7.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app_Denmo.png (100%) rename {public => pages/public}/img/drivers/onedrive_app/onedrive_app_user.png (100%) rename {public => pages/public}/img/drivers/pikpak.png (100%) rename {public => pages/public}/img/drivers/pikpak/pikpak-error-1.png (100%) rename {public => pages/public}/img/drivers/pikpak/pikpak-error-2.png (100%) rename {public => pages/public}/img/drivers/pikpak/pikpak-share-rootfolder.png (100%) rename {public => pages/public}/img/drivers/pikpak/pikpak-share.png (100%) rename {public => pages/public}/img/drivers/pikpak/pikpak-token-1.png (100%) rename {public => pages/public}/img/drivers/pikpak/pikpak-token-2.png (100%) rename {public => pages/public}/img/drivers/quark-rootid.png (100%) rename {public => pages/public}/img/drivers/quark.png (100%) rename {public => pages/public}/img/drivers/quark/quark_cookie.png (100%) rename {public => pages/public}/img/drivers/quark/quark_fileid.png (100%) rename {public => pages/public}/img/drivers/ref_token.png (100%) rename {public => pages/public}/img/drivers/s3/139.png (100%) rename {public => pages/public}/img/drivers/s3/al.png (100%) rename {public => pages/public}/img/drivers/s3/aws.png (100%) rename {public => pages/public}/img/drivers/s3/b2.png (100%) rename {public => pages/public}/img/drivers/s3/bd.png (100%) rename {public => pages/public}/img/drivers/s3/cfr2.png (100%) rename {public => pages/public}/img/drivers/s3/doge.png (100%) rename {public => pages/public}/img/drivers/s3/googleStorage1.png (100%) rename {public => pages/public}/img/drivers/s3/googleStorage2.png (100%) rename {public => pages/public}/img/drivers/s3/googleStorage3.png (100%) rename {public => pages/public}/img/drivers/s3/googleStorage4.png (100%) rename {public => pages/public}/img/drivers/s3/hw.png (100%) rename {public => pages/public}/img/drivers/s3/jd.png (100%) rename {public => pages/public}/img/drivers/s3/mini-io.png (100%) rename {public => pages/public}/img/drivers/s3/qn.png (100%) rename {public => pages/public}/img/drivers/s3/sc.png (100%) rename {public => pages/public}/img/drivers/s3/tx.png (100%) rename {public => pages/public}/img/drivers/s3/ty.png (100%) rename {public => pages/public}/img/drivers/s3/up.png (100%) rename {public => pages/public}/img/drivers/s3/v1andv2.png (100%) rename {public => pages/public}/img/drivers/s3/zijie_tos.png (100%) rename {public => pages/public}/img/drivers/teambition-cookie.png (100%) rename {public => pages/public}/img/drivers/teambition-id.png (100%) rename {public => pages/public}/img/drivers/teldrive/fetch_teldrive_cookie.png (100%) rename {public => pages/public}/img/drivers/terabox/terabox1.png (100%) rename {public => pages/public}/img/drivers/terabox/terabox2.png (100%) rename {public => pages/public}/img/drivers/terabox/terabox3.png (100%) rename {public => pages/public}/img/drivers/terabox/terabox_cookie.png (100%) rename {public => pages/public}/img/drivers/terabox/terabox_f12.png (100%) rename {public => pages/public}/img/drivers/terabox/terabox_js_token.png (100%) rename {public => pages/public}/img/drivers/tv_qrcode.png (100%) rename {public => pages/public}/img/drivers/uc/uc_cookie.png (100%) rename {public => pages/public}/img/drivers/uc/uc_fileid.png (100%) rename {public => pages/public}/img/drivers/vtencent/vtencent_close.png (100%) rename {public => pages/public}/img/drivers/vtencent/vtencent_cookie.png (100%) rename {public => pages/public}/img/drivers/webdav/webdav_Security.png (100%) rename {public => pages/public}/img/drivers/weiyun/weiyun_cookie.png (100%) rename {public => pages/public}/img/drivers/weiyun/weiyun_dir_id.png (100%) rename {public => pages/public}/img/drivers/weiyun/weiyun_fl_id.png (100%) rename {public => pages/public}/img/drivers/wopan/add-wopan.png (100%) rename {public => pages/public}/img/drivers/wopan/wopan-req.png (100%) rename {public => pages/public}/img/drivers/wopan/wopan-resp.png (100%) rename {public => pages/public}/img/drivers/wopan/wopan-session-storage.png (100%) rename {public => pages/public}/img/drivers/xunlei/SMS.png (100%) rename {public => pages/public}/img/drivers/xunlei/input.png (100%) rename {public => pages/public}/img/drivers/xunlei/output.png (100%) rename {public => pages/public}/img/drivers/xunlei/storage_error.png (100%) rename {public => pages/public}/img/drivers/xunlei/x3.png (100%) rename {public => pages/public}/img/drivers/xunlei/x4.png (100%) rename {public => pages/public}/img/drivers/xunlei/x5.png (100%) rename {public => pages/public}/img/drivers/xunlei/x6.png (100%) rename {public => pages/public}/img/drivers/xunlei/x_br_add1.png (100%) rename {public => pages/public}/img/drivers/xunlei/x_br_add2.png (100%) rename {public => pages/public}/img/drivers/xunlei/x_br_error.png (100%) rename {public => pages/public}/img/drivers/xunlei/x_br_foled.png (100%) rename {public => pages/public}/img/drivers/xunlei/xlx_name.jpg (100%) rename {public => pages/public}/img/drivers/xunlei/xlx_z1.jpg (100%) rename {public => pages/public}/img/drivers/xunlei/xlx_z2.jpg (100%) rename {public => pages/public}/img/faq/123error.png (100%) rename {public => pages/public}/img/faq/add.jpeg (100%) rename {public => pages/public}/img/faq/alist-admin.gif (100%) rename {public => pages/public}/img/faq/alist-admin.png (100%) rename {public => pages/public}/img/faq/download.png (100%) rename {public => pages/public}/img/faq/error.png (100%) rename {public => pages/public}/img/guide/installation/clawcloud-01.png (100%) rename {public => pages/public}/img/guide/installation/clawcloud-run.svg (100%) rename {public => pages/public}/img/guide/migrate/aliyun_remove1.png (100%) rename {public => pages/public}/img/guide/migrate/aliyun_remove2.png (100%) rename {public => pages/public}/img/guide/migrate/aliyun_remove3.png (100%) rename {public => pages/public}/img/guide/migrate/aliyun_remove4.jpg (100%) rename {public => pages/public}/img/guide/migrate/baidu_remove1.png (100%) rename {public => pages/public}/img/guide/migrate/baidu_remove2.png (100%) rename {public => pages/public}/img/guide/migrate/baidu_remove3.jpg (100%) rename {public => pages/public}/img/guide/migrate/google_remove1.png (100%) rename {public => pages/public}/img/guide/migrate/google_remove2.png (100%) rename {public => pages/public}/img/guide/migrate/google_remove3.png (100%) rename {public => pages/public}/img/guide/migrate/odb_remove1.jpg (100%) rename {public => pages/public}/img/guide/migrate/odp_remove1.jpg (100%) rename {public => pages/public}/img/guide/reverse_proxy/bt_delete_default_config_01.png (100%) rename {public => pages/public}/img/guide/reverse_proxy/bt_delete_default_config_02.png (100%) rename {public => pages/public}/img/guide/reverse_proxy/bt_new_website.png (100%) rename {public => pages/public}/img/guide/reverse_proxy/bt_new_website_01.png (100%) rename {public => pages/public}/img/guide/reverse_proxy/bt_reverse_proxy.png (100%) rename {public => pages/public}/img/guide/webdav/es-android.png (100%) rename {public => pages/public}/img/guide/webdav/es-ios.png (100%) rename {public => pages/public}/img/guide/webdav/fileball.png (100%) rename {public => pages/public}/img/guide/webdav/infuse.png (100%) rename {public => pages/public}/img/guide/webdav/nas.png (100%) rename {public => pages/public}/img/guide/webdav/nplayer.png (100%) rename {public => pages/public}/img/guide/webdav/potplayer.png (100%) rename {public => pages/public}/img/guide/webdav/reex.png (100%) rename {public => pages/public}/img/koyeb/db_host.png (100%) rename {public => pages/public}/img/koyeb/deployment.png (100%) rename {public => pages/public}/img/koyeb/environment.png (100%) rename {public => pages/public}/img/koyeb/file.png (100%) rename {public => pages/public}/img/koyeb/success.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppApplicationName.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppContainerEntrypoint.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppImageConfiguration.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppNetworkConfiguration.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppPortalConfiguration.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppSecurityContextConfiguration.png (100%) rename {public => pages/public}/img/truenas/InstallCustomAppStorageConfiguration.png (100%) rename {public => pages/public}/img/truenas/InstallCustomResourcesConfiguration.png (100%) rename {public => pages/public}/img/worker/create_app1.png (100%) rename {public => pages/public}/img/worker/create_app2.png (100%) rename {public => pages/public}/img/worker/create_app3.png (100%) rename {public => pages/public}/img/worker/create_app4.png (100%) rename {public => pages/public}/img/worker/create_app5.png (100%) rename {public => pages/public}/img/worker/create_app6.png (100%) rename {public => pages/public}/img/worker/edgeone_ui1.png (100%) rename {public => pages/public}/img/worker/edgeone_ui2.png (100%) rename {public => pages/public}/img/worker/edgeone_ui3.png (100%) rename {assets => pages/public}/logo.svg (100%) rename {assets => pages/public}/logo.webp (100%) create mode 100644 pages/snippets/reverse-tip.md create mode 100644 pages/snippets/tos-tip.md delete mode 100644 patches/README.md delete mode 100644 patches/valaxy-theme-press@0.25.9.patch delete mode 100644 patches/valaxy@0.25.9.patch delete mode 100644 pnpm-lock.yaml delete mode 100644 pnpm-workspace.yaml delete mode 100644 public/algolia-verify.html delete mode 100644 public/img/dev-tip/correctcontent-dark.png delete mode 100644 public/img/dev-tip/correctcontent-light.png delete mode 100644 public/img/dev-tip/correctendline-dark.png delete mode 100644 public/img/dev-tip/correctendline-light.png delete mode 100644 public/img/dev-tip/correcttitle-dark.png delete mode 100644 public/img/dev-tip/correcttitle-light.png delete mode 100644 public/img/dev-tip/correcttranslateendline-dark.png delete mode 100644 public/img/dev-tip/correcttranslateendline-light.png delete mode 100644 public/img/dev-tip/wrongcontent-dark.png delete mode 100644 public/img/dev-tip/wrongcontent-light.png delete mode 100644 public/img/dev-tip/wrongendline-dark.png delete mode 100644 public/img/dev-tip/wrongendline-light.png delete mode 100644 public/img/dev-tip/wrongtitle-dark.png delete mode 100644 public/img/dev-tip/wrongtitle-light.png delete mode 100644 public/img/dev-tip/wrongtranslateendline-dark.png delete mode 100644 public/img/dev-tip/wrongtranslateendline-light.png delete mode 100644 public/logo.svg delete mode 100644 public/logo.webp delete mode 100644 snippets/snippet-with-region.js delete mode 100644 snippets/snippet.js delete mode 100644 types/image.d.ts delete mode 100644 utils/languageDetector.ts delete mode 100644 valaxy.config.ts diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index aed1aad9b..ab984e7eb 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -18,32 +18,24 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - # submodules: true - - name: Install pnpm - uses: pnpm/action-setup@v5 - with: - version: 10.12.1 - run_install: false + # Bun version comes from `packageManager` in package.json + - name: Setup Bun + uses: oven-sh/setup-bun@v2 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: - node-version: 22 - cache: pnpm + node-version-file: .nvmrc - name: Install Deps - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Build Docs - env: - NODE_OPTIONS: --max_old_space_size=8192 - DEPLOY_PLATFORM: github - run: |- - pnpm run build + run: bun run build - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 diff --git a/.gitignore b/.gitignore index 1548e90a7..0139850d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,10 @@ -# vite-ssg -.vite-ssg-dist -.vite-ssg-temp - -temp/ -# temp pages -pages/temp/ - .DS_Store *.local -dist -dist-ssr +*.log node_modules +dist .idea/ -*.log -.valaxy -components.d.ts - -# valaxy fuse -public/valaxy-fuse-list.json - -# valaxy rss -public/feed.xml - -# valaxy rss -public/feed.json - -# valaxy rss -public/atom.xml - -# Git log prebuilt -public/git-log.json -# rollup-plugin-visualizer -stats.html \ No newline at end of file +# VitePress +.vitepress/cache +.vitepress/.temp diff --git a/.husky/pre-commit b/.husky/pre-commit index 5ee7abd87..ea5a55b6f 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -pnpm exec lint-staged +bunx lint-staged diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 71ecd69c4..000000000 --- a/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -# for pnpm -shamefully-hoist=true -strict-peer-dependencies=false diff --git a/.nvmrc b/.nvmrc index cabf43b5d..a45fd52cc 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24 \ No newline at end of file +24 diff --git a/.prettierignore b/.prettierignore index c5a1cbb4f..0a1b8d3d3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,9 +1,8 @@ dist node_modules -.valaxy -.output +.vitepress/cache coverage *.min.js *.min.css -pnpm-lock.yaml +bun.lock CHANGELOG.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts new file mode 100644 index 000000000..3f95ca9af --- /dev/null +++ b/.vitepress/config.mts @@ -0,0 +1,287 @@ +import process from 'node:process' +import { fileURLToPath } from 'node:url' +import { defineConfigWithTheme, type DefaultTheme } from 'vitepress' +import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons' +import { getContributors } from './contributors' +import { mermaidPlugin } from './plugins/mermaid' +import { themeImagePlugin } from './plugins/theme-image' +import { generateSidebar } from './sidebar' +import { fetchOpenListVersion } from './version' + +export interface ThemeConfig extends DefaultTheme.Config { + /** giscus comments, disabled for the China mainland mirror */ + comments?: boolean +} + +const COMMIT_TAG = process.env.COMMIT_TAG || (await fetchOpenListVersion()) || 'dev' +const DOCS_COMMIT_SHA = process.env.CF_PAGES_COMMIT_SHA || process.env.DOCS_COMMIT_SHA +const DOCS_ICP = process.env.DOCS_ICP || '' +const DOCS_CN = DOCS_ICP !== '' +const DOCS_BUILT_DATE = new Date().toLocaleString('zh-CN', { hour12: false }) +const SITE_URL = process.env.SITE_URL || 'https://doc.oplist.org' +const BASE = `/${(process.env.VITE_BASE || '').replace(/^\/+|\/+$/g, '')}/`.replace(/\/+/g, '/') + +const srcDir = fileURLToPath(new URL('../pages', import.meta.url)) + +// top-level sidebar sections, in display order +const SIDEBAR_CATEGORIES = [ + 'guide', + 'configuration', + 'faq', + 'seeds', + 'api', + 'migration', + 'ecosystem', +] + +const categoryText = { + en: { + configuration: 'Configuration', + guide: 'User Guide', + installation: 'Installation', + advanced: 'Advanced', + drivers: 'Storage Setup', + faq: 'FAQ', + seeds: 'Transfer Seeds', + api: 'API Documentation', + admin: 'admin', + changelog: 'Changelog', + ecosystem: 'Ecosystem', + eco_official: 'Official Ecosystem', + eco_worker: 'OpenList Worker', + }, + zh: { + configuration: '配置指南', + guide: '用户指南', + installation: '安装教程', + advanced: '高级设置', + drivers: '添加存储', + faq: 'FAQ', + seeds: '传输种子', + developer: '开发指引', + api: 'API 文档', + admin: 'admin', + changelog: 'Changelog', + ecosystem: '生态项目', + eco_official: '官方生态项目', + eco_worker: 'OpenList Worker', + }, +} + +const navText = { + en: { + guide: 'User Guide', + configuration: 'Configuration', + faq: 'FAQ', + seeds: 'Transfer Seeds', + ecosystem: 'Ecosystem', + community: 'Community', + discussions: 'Github Discussions', + telegramChannel: 'Telegram Channel', + }, + zh: { + guide: '用户指南', + configuration: '配置指南', + faq: 'FAQ', + seeds: '传输种子', + ecosystem: '生态项目', + community: '社区', + discussions: 'Github 讨论区', + telegramChannel: 'Telegram 频道', + }, +} + +function nav(prefix: string, text: typeof navText.en): DefaultTheme.NavItem[] { + return [ + { text: text.guide, link: `${prefix}/guide/`, activeMatch: `^${prefix}/guide/` }, + { + text: text.configuration, + link: `${prefix}/configuration/`, + activeMatch: `^${prefix}/configuration/`, + }, + { text: text.faq, link: `${prefix}/faq/`, activeMatch: `^${prefix}/faq/` }, + { text: text.seeds, link: `${prefix}/seeds/`, activeMatch: `^${prefix}/seeds/` }, + { text: text.ecosystem, link: `${prefix}/ecosystem/`, activeMatch: `^${prefix}/ecosystem/` }, + { + text: text.community, + items: [ + { text: text.discussions, link: 'https://github.com/OpenListTeam/OpenList/discussions' }, + { text: 'Telegram', link: 'https://t.me/OpenListTeam' }, + { text: text.telegramChannel, link: 'https://t.me/OpenListOfficial' }, + ], + }, + { + text: COMMIT_TAG, + items: [{ text: 'Release Notes', link: 'https://github.com/OpenListTeam/OpenList/releases' }], + }, + ] +} + +const footer = { + message: DOCS_COMMIT_SHA + ? `Commit ${DOCS_COMMIT_SHA.slice(0, 8)} built at ${DOCS_BUILT_DATE}` + : `Built at ${DOCS_BUILT_DATE}`, + copyright: `AGPL-3.0 Licensed | © 2022-${new Date().getFullYear()} The OpenList Projects Contributors${DOCS_ICP ? ` | ${DOCS_ICP}` : ''}`, +} + +export default defineConfigWithTheme({ + srcDir: 'pages', + srcExclude: ['**/snippets/**', 'guide/advanced/sso/**', 'en/guide/advanced/sso/**'], + outDir: 'dist', + base: BASE, + cleanUrls: true, + ignoreDeadLinks: 'localhostLinks', + lastUpdated: true, + + head: [ + ['link', { rel: 'icon', href: `${BASE}favicon.svg`, type: 'image/svg+xml' }], + ['meta', { property: 'og:site_name', content: 'OpenList Docs' }], + ['meta', { property: 'og:image', content: `${SITE_URL}/logo.svg` }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { name: 'twitter:card', content: 'summary_large_image' }], + ], + + sitemap: { + hostname: SITE_URL, + }, + + locales: { + root: { + label: '简体中文', + lang: 'zh-CN', + title: 'OpenList 文档', + description: 'OpenList 文档网站', + themeConfig: { + nav: nav('', navText.zh), + sidebar: generateSidebar({ + srcDir, + localePrefix: '', + otherLocales: ['en'], + categories: SIDEBAR_CATEGORIES, + categoryText: categoryText.zh, + }), + editLink: { + pattern: 'https://github.com/OpenListTeam/OpenList-Docs/edit/main/pages/:path', + text: '在 GitHub 上编辑此页', + }, + docFooter: { prev: '上一页', next: '下一页' }, + outline: { label: '页面导航' }, + lastUpdated: { text: '最后更新于' }, + returnToTopLabel: '回到顶部', + sidebarMenuLabel: '菜单', + darkModeSwitchLabel: '主题', + lightModeSwitchTitle: '切换到浅色模式', + darkModeSwitchTitle: '切换到深色模式', + langMenuLabel: '切换语言', + notFound: { + title: '页面未找到', + quote: '但如果你不改变方向,并且继续寻找,你可能最终会到达你所前往的地方。', + linkLabel: '前往首页', + linkText: '带我回首页', + }, + }, + }, + en: { + label: 'English', + lang: 'en', + link: '/en/', + title: 'OpenList Docs', + description: 'Documentation site for OpenList', + themeConfig: { + nav: nav('/en', navText.en), + sidebar: generateSidebar({ + srcDir, + localePrefix: 'en', + otherLocales: [], + categories: SIDEBAR_CATEGORIES, + categoryText: categoryText.en, + }), + editLink: { + pattern: 'https://github.com/OpenListTeam/OpenList-Docs/edit/main/pages/:path', + text: 'Edit this page on GitHub', + }, + }, + }, + }, + + themeConfig: { + logo: '/logo.svg', + socialLinks: [{ icon: 'github', link: 'https://github.com/OpenListTeam/OpenList' }], + comments: !DOCS_CN, + footer, + search: { + provider: 'local', + options: { + miniSearch: { + options: { + // Split Chinese text into words, the same way when indexing (Node) and + // searching (browser). Must stay self-contained: VitePress serializes it. + tokenize: (text: string) => + Array.from(new Intl.Segmenter('zh-CN', { granularity: 'word' }).segment(text)) + .filter(segment => segment.isWordLike) + .map(segment => segment.segment), + }, + searchOptions: { + // single characters from the segmenter would otherwise match almost every page + combineWith: 'AND', + }, + }, + locales: { + root: { + translations: { + button: { + buttonText: '搜索文档', + buttonAriaLabel: '搜索文档', + }, + modal: { + displayDetails: '显示详细列表', + resetButtonTitle: '清除查询条件', + backButtonTitle: '关闭搜索', + noResultsText: '没有找到相关结果', + footer: { + selectText: '选择', + selectKeyAriaLabel: '回车', + navigateText: '切换', + navigateUpKeyAriaLabel: '上方向键', + navigateDownKeyAriaLabel: '下方向键', + closeText: '关闭', + closeKeyAriaLabel: 'Esc', + }, + }, + }, + }, + }, + }, + }, + }, + + markdown: { + languageAlias: { + env: 'dotenv', + }, + config(md) { + // the plugin is typed against a newer @types/markdown-it than VitePress 1.x ships + md.use(groupIconMdPlugin as unknown as Parameters[0]) + md.use(themeImagePlugin) + md.use(mermaidPlugin) + }, + }, + + transformPageData(pageData) { + if (pageData.frontmatter.layout === 'home' || pageData.frontmatter.contributors === false) + return + pageData.contributors = getContributors(pageData.relativePath) + }, + + vite: { + plugins: [ + groupIconVitePlugin({ + customIcon: { + docker: 'vscode-icons:file-type-docker', + nginx: 'vscode-icons:file-type-nginx', + toml: 'vscode-icons:file-type-toml', + }, + }), + ], + }, +}) diff --git a/.vitepress/contributors.ts b/.vitepress/contributors.ts new file mode 100644 index 000000000..eccaf5300 --- /dev/null +++ b/.vitepress/contributors.ts @@ -0,0 +1,93 @@ +import { execFileSync } from 'node:child_process' +import { createHash } from 'node:crypto' + +declare module 'vitepress' { + interface PageData { + /** set in `transformPageData`, see `.vitepress/config.mts` */ + contributors?: Contributor[] + } +} + +export interface Contributor { + name: string + count: number + avatar: string + github?: string +} + +interface Author { + name: string + email: string + count: number +} + +/** file path (relative to repo root) -> author email -> author */ +let gitLog: Map> | undefined + +function loadGitLog(): Map> { + const files = new Map>() + let output = '' + try { + output = execFileSync('git', ['log', '--format=%x1e%an%x1f%ae', '--name-only', '--', 'pages'], { + encoding: 'utf8', + maxBuffer: 256 * 1024 * 1024, + }) + } catch (error) { + console.warn('[contributors] git log failed, contributors will be empty:', error) + return files + } + + for (const entry of output.split('\x1e').slice(1)) { + const [header, ...paths] = entry.split('\n') + const [name, rawEmail] = header.split('\x1f') + const email = rawEmail.trim().toLowerCase() + if (name.endsWith('[bot]')) continue + for (const file of paths) { + if (!file.trim()) continue + const authors = files.get(file) ?? new Map() + files.set(file, authors) + const author = authors.get(email) + if (author) author.count++ + // git log is newest first, so the first name seen is the latest one + else authors.set(email, { name, email, count: 1 }) + } + } + return files +} + +function toContributor({ name, email, count }: Author): Contributor { + const noreply = email.match(/^(?:(\d+)\+)?([^@]+)@users\.noreply\.github\.com$/) + if (noreply) { + const [, id, login] = noreply + return { + name, + count, + github: `https://github.com/${login}`, + avatar: id + ? `https://avatars.githubusercontent.com/u/${id}?s=64&v=4` + : `https://github.com/${login}.png?size=64`, + } + } + const hash = createHash('md5').update(email).digest('hex') + return { name, count, avatar: `https://www.gravatar.com/avatar/${hash}?s=64&d=identicon` } +} + +/** + * Contributors of a page, merged across both languages: before the VitePress + * migration both languages lived in the same file. + * + * @param relativePath page path relative to `pages/`, e.g. `en/guide/index.md` + */ +export function getContributors(relativePath: string): Contributor[] { + gitLog ??= loadGitLog() + const base = relativePath.replace(/^en\//, '') + const merged = new Map() + for (const file of [`pages/${base}`, `pages/en/${base}`]) { + for (const author of gitLog.get(file)?.values() ?? []) { + const existing = merged.get(author.email) + if (existing) existing.count += author.count + else merged.set(author.email, { ...author }) + } + } + return [...merged.values()].sort((a, b) => b.count - a.count).map(toContributor) +} diff --git a/.vitepress/plugins/mermaid.ts b/.vitepress/plugins/mermaid.ts new file mode 100644 index 000000000..170ce007f --- /dev/null +++ b/.vitepress/plugins/mermaid.ts @@ -0,0 +1,15 @@ +import type { MarkdownRenderer } from 'vitepress' + +/** + * Render ```mermaid fences with the client-side component. + */ +export function mermaidPlugin(md: MarkdownRenderer) { + const fence = md.renderer.rules.fence! + + md.renderer.rules.fence = (tokens, idx, options, env, self) => { + const token = tokens[idx] + if (token.info.trim() !== 'mermaid') return fence(tokens, idx, options, env, self) + + return `` + } +} diff --git a/utils/markdown-theme-image.ts b/.vitepress/plugins/theme-image.ts similarity index 91% rename from utils/markdown-theme-image.ts rename to .vitepress/plugins/theme-image.ts index 7b96c8f82..0124a3fa6 100644 --- a/utils/markdown-theme-image.ts +++ b/.vitepress/plugins/theme-image.ts @@ -1,6 +1,9 @@ -import type MarkdownIt from 'markdown-it' +import type { MarkdownRenderer } from 'vitepress' -export function markdownThemeImagePlugin(md: MarkdownIt) { +/** + * Render `![](a.png#light)` + `![](a.png#dark)` pairs as a light/dark switching image. + */ +export function themeImagePlugin(md: MarkdownRenderer) { const defaultImageRender = md.renderer.rules.image || function (tokens: any, idx: any, options: any, _env: any, renderer: any) { diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts new file mode 100644 index 000000000..49e95a915 --- /dev/null +++ b/.vitepress/sidebar.ts @@ -0,0 +1,121 @@ +import fs from 'node:fs' +import path from 'node:path' +import matter from 'gray-matter' +import type { DefaultTheme } from 'vitepress' + +/** + * Build the sidebar from page frontmatter, the same way the old Valaxy press + * theme did it: + * + * - `categories: [guide, drivers]` puts a page in the "guide > drivers" group + * - `top` sorts pages (and therefore groups) in descending order + * - a nested group of more than 8 pages without subgroups starts collapsed + */ + +interface PageEntry { + link: string + title: string + top: number + categories: string[] +} + +interface CategoryNode { + name: string + children: Map +} + +const isCategory = (item: CategoryNode | PageEntry): item is CategoryNode => 'children' in item + +function walk(dir: string): string[] { + return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => { + const full = path.join(dir, entry.name) + if (entry.isDirectory()) return entry.name === 'public' ? [] : walk(full) + return entry.name.endsWith('.md') ? [full] : [] + }) +} + +function toLink(relativePath: string): string { + return '/' + relativePath.replace(/(^|\/)index\.md$/, '$1').replace(/\.md$/, '') +} + +function readPages(srcDir: string, localePrefix: string, otherLocales: string[]): PageEntry[] { + const root = path.join(srcDir, localePrefix) + const files = walk(root).filter(file => { + const rel = path.relative(root, file) + // the root locale must not pick up the trees of the other locales + return !otherLocales.some(locale => rel.startsWith(locale + path.sep)) + }) + + const pages: PageEntry[] = [] + for (const file of files.sort()) { + const { data, content } = matter(fs.readFileSync(file, 'utf8')) + if (!Array.isArray(data.categories) || data.categories.length === 0) continue + const title = content.match(/^#\s+(.+?)(?:\s+\{#[^}]*\})?\s*$/m)?.[1] + if (!title) continue + pages.push({ + link: toLink(path.relative(srcDir, file).split(path.sep).join('/')), + title, + top: Number(data.top) || 0, + categories: data.categories.map(String), + }) + } + return pages +} + +function buildTree(pages: PageEntry[]): CategoryNode { + const root: CategoryNode = { name: 'All', children: new Map() } + // stable sort keeps path order for equal `top` + for (const page of [...pages].sort((a, b) => b.top - a.top)) { + let node = root + for (const name of page.categories) { + let next = node.children.get(name) + if (!next || !isCategory(next)) { + next = { name, children: new Map() } + node.children.set(name, next) + } + node = next + } + node.children.set(page.link, page) + } + return root +} + +function toItems( + node: CategoryNode, + categoryText: Record +): DefaultTheme.SidebarItem[] { + return [...node.children.values()].map(child => { + if (!isCategory(child)) return { text: child.title, link: child.link } + const children = [...child.children.values()] + const onlyPages = children.every(item => !isCategory(item)) + return { + text: categoryText[child.name] ?? child.name, + collapsed: onlyPages && children.length > 8, + items: toItems(child, categoryText), + } + }) +} + +export function generateSidebar(options: { + srcDir: string + /** '' for the root locale, e.g. 'en' for /en/ */ + localePrefix: string + /** directories of the other locales inside this locale's tree */ + otherLocales: string[] + /** top-level categories, in display order */ + categories: string[] + categoryText: Record +}): DefaultTheme.SidebarItem[] { + const tree = buildTree(readPages(options.srcDir, options.localePrefix, options.otherLocales)) + return options.categories.flatMap(name => { + const node = tree.children.get(name) + if (!node || !isCategory(node)) return [] + return [ + { + text: options.categoryText[name] ?? name, + collapsed: false, + items: toItems(node, options.categoryText), + }, + ] + }) +} diff --git a/.vitepress/theme/Layout.vue b/.vitepress/theme/Layout.vue new file mode 100644 index 000000000..9f1f691c8 --- /dev/null +++ b/.vitepress/theme/Layout.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/components/BackToTop.vue b/.vitepress/theme/components/BackToTop.vue similarity index 76% rename from components/BackToTop.vue rename to .vitepress/theme/components/BackToTop.vue index 5eb51b611..cbc313ed8 100644 --- a/components/BackToTop.vue +++ b/.vitepress/theme/components/BackToTop.vue @@ -4,17 +4,22 @@ v-show="isVisible" class="back-to-top-btn" @click="scrollToTop" - :aria-label="$t('tooltip.back_to_top')" - :title="$t('tooltip.back_to_top')" + :aria-label="t('tooltip.back_to_top')" + :title="t('tooltip.back_to_top')" > -
+ + + + + diff --git a/.vitepress/theme/components/Mermaid.vue b/.vitepress/theme/components/Mermaid.vue new file mode 100644 index 000000000..5bf7a6455 --- /dev/null +++ b/.vitepress/theme/components/Mermaid.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/components/OpenListDownload.vue b/.vitepress/theme/components/OpenListDownload.vue similarity index 99% rename from components/OpenListDownload.vue rename to .vitepress/theme/components/OpenListDownload.vue index d901db3b3..291d8d872 100644 --- a/components/OpenListDownload.vue +++ b/.vitepress/theme/components/OpenListDownload.vue @@ -87,7 +87,7 @@ + + + + diff --git a/components/openlist_releases_db.json b/.vitepress/theme/components/openlist_releases_db.json similarity index 100% rename from components/openlist_releases_db.json rename to .vitepress/theme/components/openlist_releases_db.json diff --git a/.vitepress/theme/i18n.ts b/.vitepress/theme/i18n.ts new file mode 100644 index 000000000..0635a0472 --- /dev/null +++ b/.vitepress/theme/i18n.ts @@ -0,0 +1,61 @@ +import { computed } from 'vue' +import { useData } from 'vitepress' + +const messages = { + en: { + wip: 'WIP, please stay tuned', + tooltip: { + back_to_top: 'Back to Top', + }, + post: { + contributors: 'Contributors', + }, + download: { + all: 'All', + os: 'Operating System', + cpu: 'CPU Architecture', + down_source: 'Download Source', + download: 'Download', + gh_proxy: 'GhProxy', + version: 'Version', + beta: 'Beta', + latest: 'Latest', + }, + }, + 'zh-CN': { + wip: '编写中,敬请期待', + tooltip: { + back_to_top: '回到顶部', + }, + post: { + contributors: '贡献者', + }, + download: { + all: '全部', + os: '操作系统', + cpu: 'CPU 架构', + down_source: '下载来源', + download: '下载', + gh_proxy: '加速下载', + version: '版本', + beta: '测试版', + latest: '最新版', + }, + }, +} as const + +type Locale = keyof typeof messages + +/** Minimal replacement for vue-i18n's `useI18n`, driven by the VitePress locale. */ +export function useI18n() { + const { lang } = useData() + const locale = computed(() => (lang.value in messages ? (lang.value as Locale) : 'en')) + + function t(key: string): string { + let value: any = messages[locale.value] + for (const part of key.split('.')) value = value?.[part] + return typeof value === 'string' ? value : key + } + + return { t, locale } +} diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts new file mode 100644 index 000000000..6ebeff58e --- /dev/null +++ b/.vitepress/theme/index.ts @@ -0,0 +1,33 @@ +import type { Theme } from 'vitepress' +import DefaultTheme from 'vitepress/theme' +import { useRoute } from 'vitepress' +import mediumZoom from 'medium-zoom' +import { nextTick, onMounted, watch } from 'vue' +import 'virtual:group-icons.css' +import './style.css' +import Layout from './Layout.vue' +import BiliBili from './components/BiliBili.vue' +import Mermaid from './components/Mermaid.vue' +import OpenListDownload from './components/OpenListDownload.vue' +import WorkInProgress from './components/WorkInProgress.vue' + +export default { + extends: DefaultTheme, + Layout, + enhanceApp({ app }) { + app.component('BiliBili', BiliBili) + app.component('Mermaid', Mermaid) + app.component('OpenListDownload', OpenListDownload) + app.component('WorkInProgress', WorkInProgress) + }, + setup() { + const route = useRoute() + const initZoom = () => + mediumZoom('.vp-doc :not(a) > img:not(.no-zoom)', { background: 'var(--vp-c-bg)' }) + onMounted(initZoom) + watch( + () => route.path, + () => nextTick(initZoom) + ) + }, +} satisfies Theme diff --git a/.vitepress/theme/language.ts b/.vitepress/theme/language.ts new file mode 100644 index 000000000..cbdd6d211 --- /dev/null +++ b/.vitepress/theme/language.ts @@ -0,0 +1,81 @@ +import { useData } from 'vitepress' +import { onMounted, watch } from 'vue' + +type Lang = 'en' | 'zh-CN' + +const STORAGE_KEY = 'openlist-docs-lang' +/** preference saved by the old Valaxy site */ +const LEGACY_STORAGE_KEY = 'valaxy-lang' +const BOT_RE = /bot|crawl|spider|slurp|headless|lighthouse|prerender/i + +function read(key: string): string | null { + try { + return localStorage.getItem(key) + } catch { + return null + } +} + +function write(key: string, value: string) { + try { + localStorage.setItem(key, value) + } catch {} +} + +function asLang(value: string | null): Lang | null { + return value === 'en' || value === 'zh-CN' ? value : null +} + +function detectBrowserLang(): Lang { + const languages = navigator.languages?.length ? navigator.languages : [navigator.language] + for (const language of languages) { + if (/^zh\b/i.test(language)) return 'zh-CN' + if (/^en\b/i.test(language)) return 'en' + } + return 'en' +} + +/** Same page in the other language: `/foo` (Chinese) <-> `/en/foo`. */ +function counterpart(pathname: string, base: string, target: Lang): string | null { + if (!pathname.startsWith(base)) return null + const rel = pathname.slice(base.length) + const isEn = rel === 'en' || rel.startsWith('en/') + if (target === 'en') return isEn ? null : `${base}en/${rel}` + return isEn ? base + rel.replace(/^en\/?/, '') : null +} + +/** + * On landing, send visitors to the language they used last time (or their + * browser language on the first visit); remember the language they browse in. + */ +export function useLanguageRedirect() { + const { lang, page, site } = useData() + + onMounted(() => { + const current = asLang(lang.value) + if ( + !current || + page.value.isNotFound || + navigator.webdriver || + BOT_RE.test(navigator.userAgent) + ) + return + + const preferred = + asLang(read(STORAGE_KEY)) ?? asLang(read(LEGACY_STORAGE_KEY)) ?? detectBrowserLang() + if (preferred !== current) { + const target = counterpart(location.pathname, site.value.base, preferred) + if (target) { + write(STORAGE_KEY, preferred) + location.replace(target + location.search + location.hash) + return + } + } + write(STORAGE_KEY, current) + }) + + watch(lang, value => { + const current = asLang(value) + if (current) write(STORAGE_KEY, current) + }) +} diff --git a/styles/index.scss b/.vitepress/theme/style.css similarity index 58% rename from styles/index.scss rename to .vitepress/theme/style.css index 56c202306..9e417aef1 100644 --- a/styles/index.scss +++ b/.vitepress/theme/style.css @@ -1,35 +1,14 @@ -@keyframes fly { - from { - transform: translateY(0.5em); - } - - to { - transform: translateY(-0.5em); - } -} - -.fly-animation { - animation: fly 0.8s ease-in-out infinite alternate; -} - -// override - -// Main Color +/* Brand colors */ :root { --vp-c-brand-1: hsl(196, 55%, 45%); --vp-c-brand-2: hsl(196, 59%, 51%); --vp-c-brand-3: hsl(196, 54%, 57%); --vp-c-brand-soft: hsla(196, 100%, 70%, 0.14); - --va-c-brand: var(--vp-c-brand-1); - --va-c-brand-1: var(--vp-c-brand-1); - --va-c-brand-2: var(--vp-c-brand-2); - --va-c-brand-3: var(--vp-c-brand-3); - --va-c-brand-soft: var(--vp-c-brand-soft); - - --va-c-primary: var(--vp-c-brand-1); - --va-c-primary-light: var(--vp-c-brand-2); - --va-c-primary-lighter: var(--vp-c-brand-3); + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: linear-gradient(to right, #99f6e4, #38bdf8); + --vp-home-hero-image-background-image: linear-gradient(-45deg, #99f6e4 50%, #38bdf8 50%); + --vp-home-hero-image-filter: blur(56px); } .dark { @@ -37,34 +16,28 @@ --vp-c-brand-2: hsl(196, 74%, 63%); --vp-c-brand-3: hsl(196, 70%, 55%); --vp-c-brand-soft: hsla(196, 100%, 70%, 0.16); -} -// Background -html.dark { - --va-c-bg: hsl(196, 5%, 11%); - --va-c-bg-light: hsl(196, 10%, 14%); - --va-c-bg-alt: hsl(196, 4%, 9%); - --va-c-bg-soft: hsl(196, 10%, 14%); - --va-c-bg-mute: hsl(0, 0%, 18%); -} - -.dark { --vp-c-bg: hsl(196, 7%, 11%); --vp-c-bg-alt: hsl(196, 4%, 9%); --vp-c-bg-elv: hsl(196, 10%, 14%); --vp-c-bg-soft: hsl(196, 10%, 14%); } -// Link -:root { - --va-c-link: var(--vp-c-brand-1); +@keyframes fly { + from { + transform: translateY(0.5em); + } + + to { + transform: translateY(-0.5em); + } } -html.dark { - --va-c-link: var(--vp-c-brand-1); +.fly-animation { + animation: fly 0.8s ease-in-out infinite alternate; } -// Theme image +/* Theme images: ![](a.png#light) ![](a.png#dark), see .vitepress/plugins/theme-image.ts */ .theme-image-container { position: relative; display: inline-block; @@ -72,9 +45,7 @@ html.dark { } .theme-image-light, -.theme-image-dark, -img[src$='#light'], -img[src$='#dark'] { +.theme-image-dark { max-width: 100%; height: auto; transition: opacity 0.3s ease-in-out; @@ -100,18 +71,42 @@ img[src$='#dark'] { display: block; } -img[src$='#light'] { +img.theme-image-light { display: block; } -img[src$='#dark'] { +img.theme-image-dark { display: none; } -.dark img[src$='#light'] { +.dark img.theme-image-light { display: none; } -.dark img[src$='#dark'] { +.dark img.theme-image-dark { display: block; } + +/* medium-zoom above the nav bar */ +.medium-zoom-overlay { + z-index: 30; +} + +.medium-zoom-image--opened { + z-index: 31; +} + +/* Privacy policy & terms of service */ +.statement-page .vp-doc h1 { + margin-bottom: 2rem; + padding: 1rem 0; + border-radius: 1rem; + background: var(--vp-c-brand-3); + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.3); + color: white; + text-align: center; +} + +.statement-page .vp-doc h2 { + color: var(--vp-c-brand-1); +} diff --git a/utils/version.ts b/.vitepress/version.ts similarity index 100% rename from utils/version.ts rename to .vitepress/version.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79fb3d004..ef982fe30 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,39 +1,65 @@ # 文档编写说明 -- Frontmatter 说明 +本站使用 [VitePress](https://vitepress.dev/zh/) 构建,其 [Markdown 扩展](https://vitepress.dev/zh/guide/markdown) 均可直接使用。 + +## 目录结构 + +- `pages/`: 中文页面 (站点根路径) +- `pages/en/`: 英文页面 (`/en/` 路径下),与中文页面使用相同的相对路径,例如 `pages/guide/drivers/s3.md` 对应 `pages/en/guide/drivers/s3.md` +- `pages/snippets/`、`pages/en/snippets/`: 可复用片段,通过 `` (英文页面为 `@/en/snippets/xxx.md`) 引入 +- `pages/public/`: 图片等静态资源,引用时以 `/` 开头,例如 `/img/xxx.png` +- `.vitepress/`: 站点配置与主题 + +修改页面时请同时更新中英文两个文件。 + +## 页面格式 ```md --- -title: - en: example - zh-CN: 示例 categories: - - example-category + - guide + - drivers top: 1000 --- + +# 示例 + +正文…… ``` 其中: -- `title`: 标题, 需要不同语言版本使用 `_` 分隔语言 ISO 编号 -- `categories`: 类别 +- 标题: 正文第一行的一级标题 (`# 标题`) 即为页面标题, 不要在 frontmatter 中添加 `title`. +- `categories`: 类别, 多级类别对应侧边栏中的多级分组. **必须添加类别**, 否则不会显示于侧边栏 (`sidebar`). - 可用的类别包括: + 顶级类别包括 `guide`、`configuration`、`faq`、`seeds`、`api`、`ecosystem`; 常用的子类别包括: - `installation`: 安装教程相关 - `drivers`: 驱动配置细节相关 - `advanced`: 高级操作相关 - - `api`: API 文档相关 - - `configuration`: 配置文件细节相关 - - `faq`: 常见问题相关 + + 新增类别时, 需要在 `.vitepress/config.mts` 的 `categoryText` 中添加中英文名称. - `top`: 按 `top` 由高到低排列. - 应当注意: 类别排序也是先按 `top` 由高到低排列的 (feature?). + 应当注意: 类别排序也是先按 `top` 由高到低排列的 (以类别中 `top` 最高的页面为准). 预留: - 10000-9001: 安装教程相关 - 9000-8001: 驱动配置相关 -- valaxy 并不能识别子文件夹, 子文件夹必须添加 index.md 文件 (留空即可), 否则构建失败. +## 注意事项 + +- 容器嵌套时, 外层容器需要使用更多的冒号, 否则第一个 `:::` 会直接关闭外层容器: + + ```md + :::: details 示例 + ::: tip + 内层提示 + ::: + :::: + ``` + +- 在图片链接后添加 `#light` / `#dark`, 图片只会在对应的颜色模式下显示. +- 构建时会检查站内死链, 链接到目录首页时请以 `/` 结尾, 例如 `/guide/`. diff --git a/README.cn.md b/README.cn.md index f986ac027..4adae07a1 100644 --- a/README.cn.md +++ b/README.cn.md @@ -22,16 +22,14 @@ OpenList 是一个现代化的基于 Web 的文件管理解决方案,具有以 ## 📖 文档 -这是 OpenList 的新文档。使用 Valaxy 构建。对于旧文档(基于 Alist Docs),请访问 [OpenList Docs Legacy](https://github.com/OpenListTeam/docs)。 +这是 OpenList 的新文档。使用 [VitePress](https://vitepress.dev/zh/) 构建。对于旧文档(基于 Alist Docs),请访问 [OpenList Docs Legacy](https://github.com/OpenListTeam/docs)。 ## 🛠️ 开发 -本文档站点使用 [Valaxy](https://github.com/YunYouJun/valaxy) 和 [valaxy-theme-press](https://github.com/YunYouJun/valaxy/tree/main/packages/valaxy-theme-press) 构建。 - ### 环境要求 -- Node.js 18+ -- pnpm(推荐的包管理器) +- Node.js 24+ +- [Bun](https://bun.sh/)(包管理器) ### 本地开发 @@ -41,22 +39,24 @@ git clone https://github.com/OpenListTeam/OpenList-Docs.git cd OpenList-Docs # 安装依赖 -pnpm install +bun install # 启动开发服务器 -pnpm dev +bun run dev ``` -文档站点将在 `http://localhost:4859` 可用。 +文档站点将在 `http://localhost:5173` 可用。 + +中文页面位于 `pages/`,英文页面位于 `pages/en/`,页面组织方式见[贡献指南](./CONTRIBUTING.md)。 ### 构建 ```bash -# 生产环境构建 -pnpm build +# 生产环境构建(输出到 dist/) +bun run build # 预览生产构建 -pnpm serve +bun run preview ``` ## 🤝 贡献 diff --git a/README.md b/README.md index 6d2fd3b81..ae0e0a4d5 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,14 @@ OpenList is a modern, web-based file management solution that allows you to: ## 📖 Documentation -This is the new official documentation site for OpenList. It is built with Valaxy. For old documentation(based on Alist Docs) please visit [OpenList Docs Legacy](https://github.com/OpenListTeam/docs). +This is the new official documentation site for OpenList. It is built with [VitePress](https://vitepress.dev/). For old documentation(based on Alist Docs) please visit [OpenList Docs Legacy](https://github.com/OpenListTeam/docs). ## 🛠️ Development -This documentation site is built with [Valaxy](https://github.com/YunYouJun/valaxy) and [valaxy-theme-press](https://github.com/YunYouJun/valaxy/tree/main/packages/valaxy-theme-press). - ### Prerequisites -- Node.js 18+ -- pnpm (recommended package manager) +- Node.js 24+ +- [Bun](https://bun.sh/) (package manager) ### Local Development @@ -41,22 +39,24 @@ git clone https://github.com/OpenListTeam/OpenList-Docs.git cd OpenList-Docs # Install dependencies -pnpm install +bun install # Start development server -pnpm dev +bun run dev ``` -The documentation site will be available at `http://localhost:4859`. +The documentation site will be available at `http://localhost:5173`. + +Chinese pages are in `pages/`, English pages are in `pages/en/`. See the [Contributing Guide](./CONTRIBUTING.md) for how pages are organized. ### Building ```bash -# Build for production -pnpm build +# Build for production (output: dist/) +bun run build # Preview production build -pnpm serve +bun run preview ``` ## 🤝 Contributing diff --git a/assets/sites.ts b/assets/sites.ts deleted file mode 100644 index 560b7f706..000000000 --- a/assets/sites.ts +++ /dev/null @@ -1 +0,0 @@ -export const sites = [] diff --git a/bun.lock b/bun.lock new file mode 100644 index 000000000..dfd3eebe8 --- /dev/null +++ b/bun.lock @@ -0,0 +1,663 @@ +{ + "lockfileVersion": 2, + "configVersion": 1, + "workspaces": { + "": { + "name": "openlist-docs", + "dependencies": { + "@giscus/vue": "^3.1.1", + "gray-matter": "^4.0.3", + "medium-zoom": "^1.1.0", + "mermaid": "^12.0.0", + "vitepress": "^1.6.4", + "vitepress-plugin-group-icons": "^1.7.6", + "vue": "^3.5.43", + }, + "devDependencies": { + "@types/node": "^24.19.0", + "husky": "^9.1.7", + "lint-staged": "^17.6.0", + "prettier": "^3.9.9", + }, + }, + }, + "packages": { + "@algolia/abtesting": ["@algolia/abtesting@1.25.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-rSTin9Uta23uaewYVQEp8XI9T3iA/zrg0/1G2vhf8oFFDxFL5vybnZ5IQwsVAg4JpKxPX4/WYNKdcfWrZymk7w=="], + + "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.17.7", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="], + + "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A=="], + + "@algolia/autocomplete-preset-algolia": ["@algolia/autocomplete-preset-algolia@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA=="], + + "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="], + + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-bm2XN0hCSMYwStSsCBT0/PUB2BDxoyR1Lnub3c392HMEy9bi8PUSW8vR6zltVEKWG2t4PQFWMD5C07bmJxGgPg=="], + + "@algolia/client-analytics": ["@algolia/client-analytics@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-XOFPOTa69WuqHR6c5tMgnUUwwqQgNSzMpxmhrgA9KmxRf8WIqEa0cokHJvohk5CYb7CZx0xeSL6Bk2IUJ7Lv7Q=="], + + "@algolia/client-common": ["@algolia/client-common@5.59.0", "", {}, "sha512-PC8ipLOYFKRTfIUY1J3FJxS6ryzWziaXmIX9/sNMoUR8L+XhF7hX2QAeUI87YVl5zujvlYTSOb+HAIqKXDjyHQ=="], + + "@algolia/client-insights": ["@algolia/client-insights@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-yFNcCMM5fHiyoR0HuxMrzy+VjDcmhFUZTm2IJ2DHwGsVH3B5SEob4zTmeEZ3j/AZqNnmrJOCKl/tJnBg7+84bA=="], + + "@algolia/client-personalization": ["@algolia/client-personalization@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-GYja6HkDt2VrQhWmB2cLx3Z5fDwI9no7q+xwCWcFrTPm5CLH9QZvK0XLO9co1FcNIDxxkMaP3AXM39/XcecEOg=="], + + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-Wofg7bMpWh8N5qDDZs0wy6whc+KMmdNsxgrIGp9Ug2s1Bka0uq7AjyckdxReKPHLA0Q1qH8X/SNh0wn2t2X2Lw=="], + + "@algolia/client-search": ["@algolia/client-search@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-fHnALZfbEnODczGk14Y/1YBRApp6UEpZUTexGcMUPzY7RDc7q4HN2Y6jh0KUG8Jo9B+q+wOoQEc3ziR0ErbuFg=="], + + "@algolia/ingestion": ["@algolia/ingestion@1.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-Fa38s1mHgoaLCT117sfJ6P78rtxUt93CYxBYpq1VOIcslaF+cH+1h5uikAPsKfxLnsuEDZHiBiaI6eNPTsQMRA=="], + + "@algolia/monitoring": ["@algolia/monitoring@1.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-NyNsRSqM2tF1MX7ZGw/j4rduoEJiQ5wfvbSo/CFVdEzYCjyxbGFRPOZyS/GETJG9rk1TdHOq8NZqKqk/u1fm4g=="], + + "@algolia/recommend": ["@algolia/recommend@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-nXBK2uygWtvbCOffMWqqfjjcEtp9enDKY5/2Pw/Hhw8VVaOyK2ThbGK04bNX/Le+qoK1VHYkodWGVTKfw0Otkw=="], + + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0" } }, "sha512-yb+4afX/zja8QwX0KmV4/ae2kyYkRknsE79CRusYS2U5D8qwn2wq0cn1x12f3tm3F3+5FgmDdbuGdQTuCLSKMQ=="], + + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0" } }, "sha512-Lp52TmpA1QtNmdHzs505Xwf4tgII7RAapkDSF9AAtWIBETcGaTIaXci4Rd9nS6SdaWK1BWTSqAPp5wzh6UN3Ag=="], + + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.59.0", "", { "dependencies": { "@algolia/client-common": "5.59.0" } }, "sha512-YYHLEs5rC6oRTFwT7bJaKBR9NSFzikeVHGAT1ffATmUQzR8XqyhHXjoieAUoR7fBU6I5cfy1FVeh5GoRgriuzA=="], + + "@antfu/install-pkg": ["@antfu/install-pkg@2.1.0", "", { "dependencies": { "package-manager-detector": "^1.8.0", "tinyexec": "^1.3.1" } }, "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/parser": ["@babel/parser@7.29.9", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA=="], + + "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], + + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.1.2", "", { "dependencies": { "@chevrotain/gast": "11.1.2", "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q=="], + + "@chevrotain/gast": ["@chevrotain/gast@11.1.2", "", { "dependencies": { "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g=="], + + "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.1.2", "", {}, "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw=="], + + "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], + + "@chevrotain/utils": ["@chevrotain/utils@11.1.2", "", {}, "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA=="], + + "@docsearch/css": ["@docsearch/css@3.8.2", "", {}, "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ=="], + + "@docsearch/js": ["@docsearch/js@3.8.2", "", { "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ=="], + + "@docsearch/react": ["@docsearch/react@3.8.2", "", { "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + + "@giscus/vue": ["@giscus/vue@3.1.1", "", { "dependencies": { "giscus": "^1.6.0" }, "peerDependencies": { "vue": ">=3.2.0" } }, "sha512-xgsc93MibRQ0d1glBxN1BrEREHTIf3BPYs/3R+UZHNqawFFoWBV6iJ7uVCXMqoEKZdE2c78B3aKt5gfGqo8I5A=="], + + "@iconify-json/logos": ["@iconify-json/logos@1.2.14", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-O36DicXkgAMT6NAsH7MTWlOql8NktHz9fBCItTjz6L/9gyRXw4vQj6qTeVgww5UZWiVugUV1MhVFA/g9HWIkBw=="], + + "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.97", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-Z6PmM6jvWVU7rmLzqbxCLJA7SeqCnK6lMHXtjyzHPLZebwuoojBEcHHycppahDzEeN9xujRkrV5J8QpOhMyNrg=="], + + "@iconify-json/vscode-icons": ["@iconify-json/vscode-icons@1.2.82", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-J4TIauES5jhw7Ij38qhH5JoE/6MciTWz2Wz6KPiVj5kgP07mI4ai9MYpl+bn991LiqC3uW6YqNrIk0jlabT3ug=="], + + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@3.1.7", "", { "dependencies": { "@antfu/install-pkg": "^2.0.1", "@iconify/types": "^2.0.0", "import-meta-resolve": "^4.2.0" } }, "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "@lit-labs/ssr-dom-shim": ["@lit-labs/ssr-dom-shim@1.6.0", "", {}, "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ=="], + + "@lit/reactive-element": ["@lit/reactive-element@2.1.2", "", { "dependencies": { "@lit-labs/ssr-dom-shim": "^1.5.0" } }, "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A=="], + + "@mermaid-js/parser": ["@mermaid-js/parser@2.0.0", "", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-K8BeapFUfrfxbRAUQAG5oBOCwo1+bNWzaVnPxTCutkfrTBn6T/j91FIhqxWJ32SUeQ9T3iy1zcmPZ5ROZEvDrg=="], + + "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.63.5", "", { "os": "android", "cpu": "arm" }, "sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.63.5", "", { "os": "android", "cpu": "arm64" }, "sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.63.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.63.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.63.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.63.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.63.5", "", { "os": "linux", "cpu": "arm" }, "sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.63.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.63.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.63.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.63.5", "", { "os": "linux", "cpu": "none" }, "sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.63.5", "", { "os": "linux", "cpu": "none" }, "sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.63.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.63.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.63.5", "", { "os": "linux", "cpu": "none" }, "sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.63.5", "", { "os": "linux", "cpu": "none" }, "sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.63.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.63.5", "", { "os": "linux", "cpu": "x64" }, "sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.63.5", "", { "os": "linux", "cpu": "x64" }, "sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.63.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.63.5", "", { "os": "none", "cpu": "arm64" }, "sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.63.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.63.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.63.5", "", { "os": "win32", "cpu": "x64" }, "sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.63.5", "", { "os": "win32", "cpu": "x64" }, "sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw=="], + + "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], + + "@shikijs/langs": ["@shikijs/langs@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="], + + "@shikijs/themes": ["@shikijs/themes@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="], + + "@shikijs/transformers": ["@shikijs/transformers@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="], + + "@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="], + + "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="], + + "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="], + + "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="], + + "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="], + + "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="], + + "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="], + + "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="], + + "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="], + + "@types/d3-geo": ["@types/d3-geo@3.1.1", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="], + + "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="], + + "@types/d3-random": ["@types/d3-random@3.0.4", "", {}, "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="], + + "@types/d3-selection": ["@types/d3-selection@3.0.12", "", {}, "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw=="], + + "@types/d3-shape": ["@types/d3-shape@3.2.0", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="], + + "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], + + "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], + + "@types/markdown-it": ["@types/markdown-it@14.2.0", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], + + "@types/node": ["@types/node@24.19.0", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.4.0", "", {}, "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ=="], + + "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="], + + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="], + + "@vue/compiler-core": ["@vue/compiler-core@3.5.43", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.43", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.43", "", { "dependencies": { "@vue/compiler-core": "3.5.43", "@vue/shared": "3.5.43" } }, "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.43", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-core": "3.5.43", "@vue/compiler-dom": "3.5.43", "@vue/compiler-ssr": "3.5.43", "@vue/shared": "3.5.43", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.28", "source-map-js": "^1.2.1" } }, "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.43", "", { "dependencies": { "@vue/compiler-dom": "3.5.43", "@vue/shared": "3.5.43" } }, "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg=="], + + "@vue/devtools-api": ["@vue/devtools-api@7.7.10", "", { "dependencies": { "@vue/devtools-kit": "^7.7.10" } }, "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA=="], + + "@vue/devtools-kit": ["@vue/devtools-kit@7.7.10", "", { "dependencies": { "@vue/devtools-shared": "^7.7.10", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw=="], + + "@vue/devtools-shared": ["@vue/devtools-shared@7.7.10", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ=="], + + "@vue/reactivity": ["@vue/reactivity@3.5.43", "", { "dependencies": { "@vue/shared": "3.5.43" } }, "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA=="], + + "@vue/runtime-core": ["@vue/runtime-core@3.5.43", "", { "dependencies": { "@vue/reactivity": "3.5.43", "@vue/shared": "3.5.43" } }, "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw=="], + + "@vue/runtime-dom": ["@vue/runtime-dom@3.5.43", "", { "dependencies": { "@vue/reactivity": "3.5.43", "@vue/runtime-core": "3.5.43", "@vue/shared": "3.5.43", "csstype": "^3.2.3" } }, "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw=="], + + "@vue/server-renderer": ["@vue/server-renderer@3.5.43", "", { "dependencies": { "@vue/compiler-ssr": "3.5.43", "@vue/runtime-dom": "3.5.43", "@vue/shared": "3.5.43" } }, "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ=="], + + "@vue/shared": ["@vue/shared@3.5.43", "", {}, "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q=="], + + "@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="], + + "@vueuse/integrations": ["@vueuse/integrations@12.8.2", "", { "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "focus-trap", "fuse.js", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g=="], + + "@vueuse/metadata": ["@vueuse/metadata@12.8.2", "", {}, "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="], + + "@vueuse/shared": ["@vueuse/shared@12.8.2", "", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="], + + "algoliasearch": ["algoliasearch@5.59.0", "", { "dependencies": { "@algolia/abtesting": "1.25.0", "@algolia/client-abtesting": "5.59.0", "@algolia/client-analytics": "5.59.0", "@algolia/client-common": "5.59.0", "@algolia/client-insights": "5.59.0", "@algolia/client-personalization": "5.59.0", "@algolia/client-query-suggestions": "5.59.0", "@algolia/client-search": "5.59.0", "@algolia/ingestion": "1.59.0", "@algolia/monitoring": "1.59.0", "@algolia/recommend": "5.59.0", "@algolia/requester-browser-xhr": "5.59.0", "@algolia/requester-fetch": "5.59.0", "@algolia/requester-node-http": "5.59.0" } }, "sha512-wUXzaeI7B526W4y1gFg3lcxgDZ67XSgRJIiellYWOas/pLpO7rOtxm9Gr2E/C8aiSDXIgx1q5TdSsvK67Uakqw=="], + + "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "chevrotain": ["chevrotain@11.1.2", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.1.2", "@chevrotain/gast": "11.1.2", "@chevrotain/regexp-to-ast": "11.1.2", "@chevrotain/types": "11.1.2", "@chevrotain/utils": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + + "copy-anything": ["copy-anything@4.1.4", "", {}, "sha512-/LLeW0CtsoSybVkEpYjifB2Pis8DUSuwY7QD9FiR98fWkdbdrACw3ilckfnAHMecQ+RFEdZlRt6eCXhYHJbl/g=="], + + "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "cytoscape": ["cytoscape@3.34.3", "", {}, "sha512-yfYGhRcGAntq6YBD583j4n0Eg3jIxvWmZtz/5uz9UYkeIStSlMxuUja+ec5j3iBD8nv1rwaOAYMW09tBdkSeaQ=="], + + "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="], + + "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="], + + "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="], + + "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="], + + "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="], + + "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "dagre-d3-es": ["dagre-d3-es@7.0.14", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg=="], + + "dayjs": ["dayjs@1.11.23", "", {}, "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ=="], + + "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "dompurify": ["dompurify@3.4.16", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ=="], + + "elkjs": ["elkjs@0.9.3", "", {}, "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ=="], + + "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], + + "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + + "es-toolkit": ["es-toolkit@1.52.0", "", {}, "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA=="], + + "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "focus-trap": ["focus-trap@7.8.0", "", { "dependencies": { "tabbable": "^6.4.0" } }, "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "giscus": ["giscus@1.6.0", "", { "dependencies": { "lit": "^3.2.1" } }, "sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ=="], + + "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], + + "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], + + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + + "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "js-yaml": ["js-yaml@3.15.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w=="], + + "katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="], + + "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="], + + "lint-staged": ["lint-staged@17.6.0", "", { "dependencies": { "picomatch": "^4.0.7", "string-argv": "^0.3.2", "tinyexec": "^1.3.1" }, "optionalDependencies": { "yaml": "^2.9.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-nOhBfYkZEwkFtNzD5pK9s9LkcWoxJ+whLzEnhqi9nl7zTB5n7tg8+9iYgMGx2Wm3VlLu8cbNj0RACzY+nWi5iQ=="], + + "lit": ["lit@3.3.3", "", { "dependencies": { "@lit/reactive-element": "^2.1.0", "lit-element": "^4.2.0", "lit-html": "^3.3.0" } }, "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw=="], + + "lit-element": ["lit-element@4.2.2", "", { "dependencies": { "@lit-labs/ssr-dom-shim": "^1.5.0", "@lit/reactive-element": "^2.1.0", "lit-html": "^3.3.0" } }, "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w=="], + + "lit-html": ["lit-html@3.3.3", "", { "dependencies": { "@types/trusted-types": "^2.0.2" } }, "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA=="], + + "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="], + + "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "medium-zoom": ["medium-zoom@1.1.0", "", {}, "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ=="], + + "mermaid": ["mermaid@12.0.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^2.0.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "chevrotain": "~11.1.2", "cytoscape": "^3.34.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.21", "dompurify": "^3.4.12", "elkjs": "^0.9.3", "es-toolkit": "^1.45.1", "katex": "^0.16.47", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-/wQXC9iBxoGV8p3erbvaXs9h77VyLDBH6GdayVjj3hEcSQhFU4N1WUhUppotCEqlIxI2pRMwjwBSwTB1MfZBgQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.3", "", {}, "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg=="], + + "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], + + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "nanoid": ["nanoid@3.3.19", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="], + + "oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], + + "package-manager-detector": ["package-manager-detector@1.8.0", "", {}, "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A=="], + + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + + "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + + "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + + "postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="], + + "preact": ["preact@10.29.8", "", { "peerDependencies": { "preact-render-to-string": ">=5" }, "optionalPeers": ["preact-render-to-string"] }, "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q=="], + + "prettier": ["prettier@3.9.9", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg=="], + + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], + + "rollup": ["rollup@4.63.5", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.63.5", "@rollup/rollup-android-arm64": "4.63.5", "@rollup/rollup-darwin-arm64": "4.63.5", "@rollup/rollup-darwin-x64": "4.63.5", "@rollup/rollup-freebsd-arm64": "4.63.5", "@rollup/rollup-freebsd-x64": "4.63.5", "@rollup/rollup-linux-arm-gnueabihf": "4.63.5", "@rollup/rollup-linux-arm-musleabihf": "4.63.5", "@rollup/rollup-linux-arm64-gnu": "4.63.5", "@rollup/rollup-linux-arm64-musl": "4.63.5", "@rollup/rollup-linux-loong64-gnu": "4.63.5", "@rollup/rollup-linux-loong64-musl": "4.63.5", "@rollup/rollup-linux-ppc64-gnu": "4.63.5", "@rollup/rollup-linux-ppc64-musl": "4.63.5", "@rollup/rollup-linux-riscv64-gnu": "4.63.5", "@rollup/rollup-linux-riscv64-musl": "4.63.5", "@rollup/rollup-linux-s390x-gnu": "4.63.5", "@rollup/rollup-linux-x64-gnu": "4.63.5", "@rollup/rollup-linux-x64-musl": "4.63.5", "@rollup/rollup-openbsd-x64": "4.63.5", "@rollup/rollup-openharmony-arm64": "4.63.5", "@rollup/rollup-win32-arm64-msvc": "4.63.5", "@rollup/rollup-win32-ia32-msvc": "4.63.5", "@rollup/rollup-win32-x64-gnu": "4.63.5", "@rollup/rollup-win32-x64-msvc": "4.63.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw=="], + + "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="], + + "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="], + + "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], + + "shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "speakingurl": ["speakingurl@14.0.1", "", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], + + "stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], + + "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="], + + "tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="], + + "tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "ts-dedent": ["ts-dedent@2.3.0", "", {}, "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg=="], + + "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "uuid": ["uuid@14.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], + + "vitepress": ["vitepress@1.6.4", "", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg=="], + + "vitepress-plugin-group-icons": ["vitepress-plugin-group-icons@1.7.6", "", { "dependencies": { "@iconify-json/logos": "^1.2.11", "@iconify-json/vscode-icons": "^1.2.67", "@iconify/utils": "^3.1.4" }, "peerDependencies": { "vite": ">=3" }, "optionalPeers": ["vite"] }, "sha512-fXSpQAYHpFpEsZLKWQAwbRx/+0uRSHmY58YNAQcz0AVirGsM6w22jtSmQhOGLEj8ZKyeRtTJrpx6otUI6wvoaQ=="], + + "vue": ["vue@3.5.43", "", { "dependencies": { "@vue/compiler-dom": "3.5.43", "@vue/compiler-sfc": "3.5.43", "@vue/runtime-dom": "3.5.43", "@vue/server-renderer": "3.5.43", "@vue/shared": "3.5.43" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng=="], + + "yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], + + "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + + "dagre-d3-es/lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], + + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], + + "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + } +} diff --git a/components/AddonGallery.vue b/components/AddonGallery.vue deleted file mode 100644 index d3b477325..000000000 --- a/components/AddonGallery.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - diff --git a/components/AddonGalleryCard.vue b/components/AddonGalleryCard.vue deleted file mode 100644 index 2b86d56c1..000000000 --- a/components/AddonGalleryCard.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - diff --git a/components/AppLink.vue b/components/AppLink.vue deleted file mode 100644 index 057f9cbb7..000000000 --- a/components/AppLink.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/components/AutoLanguageDetector.vue b/components/AutoLanguageDetector.vue deleted file mode 100644 index 88de7ac62..000000000 --- a/components/AutoLanguageDetector.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/components/BrandIcon.vue b/components/BrandIcon.vue deleted file mode 100644 index 2c243d563..000000000 --- a/components/BrandIcon.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/components/CustomVueDemo.vue b/components/CustomVueDemo.vue deleted file mode 100644 index c25b38265..000000000 --- a/components/CustomVueDemo.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - - - diff --git a/components/InlineIcon.vue b/components/InlineIcon.vue deleted file mode 100644 index 97cf31455..000000000 --- a/components/InlineIcon.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/components/OpenListContributors.vue b/components/OpenListContributors.vue deleted file mode 100644 index 5193e38b1..000000000 --- a/components/OpenListContributors.vue +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/components/OpenListLogo.vue b/components/OpenListLogo.vue deleted file mode 100644 index 68871c068..000000000 --- a/components/OpenListLogo.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - diff --git a/components/PressButton.vue b/components/PressButton.vue deleted file mode 100644 index bad1a51aa..000000000 --- a/components/PressButton.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - diff --git a/components/PressGetStarted.vue b/components/PressGetStarted.vue deleted file mode 100644 index e5dccbf5b..000000000 --- a/components/PressGetStarted.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - - - diff --git a/components/PressHome.vue b/components/PressHome.vue deleted file mode 100644 index 7e7bdf1ef..000000000 --- a/components/PressHome.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/components/PressHomeHero.vue b/components/PressHomeHero.vue deleted file mode 100644 index 76b5e6a19..000000000 --- a/components/PressHomeHero.vue +++ /dev/null @@ -1,72 +0,0 @@ - - - - - diff --git a/components/PressNavBar.vue b/components/PressNavBar.vue deleted file mode 100644 index 426657c1c..000000000 --- a/components/PressNavBar.vue +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - diff --git a/components/PressNavScreenMenuLink.vue b/components/PressNavScreenMenuLink.vue deleted file mode 100644 index 2b0ae2a6e..000000000 --- a/components/PressNavScreenMenuLink.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - diff --git a/components/ThemeImage.vue b/components/ThemeImage.vue deleted file mode 100644 index c4b3dec0c..000000000 --- a/components/ThemeImage.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - - - diff --git a/components/ThemeImagePair.vue b/components/ThemeImagePair.vue deleted file mode 100644 index c6f0b5e33..000000000 --- a/components/ThemeImagePair.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - - - diff --git a/components/WorkInProgress.vue b/components/WorkInProgress.vue deleted file mode 100644 index 431febf02..000000000 --- a/components/WorkInProgress.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - - - diff --git a/components/api/ApiDocsRedirect.vue b/components/api/ApiDocsRedirect.vue deleted file mode 100644 index 464688b29..000000000 --- a/components/api/ApiDocsRedirect.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/layouts/default.vue b/layouts/default.vue deleted file mode 100644 index bfb6f7a8d..000000000 --- a/layouts/default.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - - - diff --git a/layouts/home.vue b/layouts/home.vue deleted file mode 100644 index 2e67333ae..000000000 --- a/layouts/home.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/layouts/statement.vue b/layouts/statement.vue deleted file mode 100644 index be3f8cc6b..000000000 --- a/layouts/statement.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - diff --git a/locales/en.yml b/locales/en.yml deleted file mode 100644 index 297de331a..000000000 --- a/locales/en.yml +++ /dev/null @@ -1,76 +0,0 @@ -siteConfig: - title: OpenList Docs - description: Documentation site for OpenList - -# ! Navigation bar search entry -search: - tip: search - -# ! Navigation menu -nav: - configuration: Configuration - guide: User Guide - faq: FAQ - seeds: Transfer Seeds - community: - title: Community - discussions: Github Discussions - telegram: Telegram - telegram_channel: Telegram Channel - github: GitHub - ecosystem: Ecosystem - -# ! Category titles (setting `title` in index.md's frontmatter will not work) -category: - configuration: Configuration - # User Guide - guide: User Guide - installation: Installation - advanced: Advanced - drivers: Storage Setup - # FAQ - faq: FAQ - seeds: Transfer Seeds - # Developer Guide - api: API Documentation - admin: admin - # Changelog - changelog: Changelog - # Ecosystem - ecosystem: Ecosystem - eco_official: Official Ecosystem - eco_worker: OpenList Worker - -# ! Index page (layout: hero) -hero: - banner: 🗂️ A file list program that supports multiple storage, powered by Gin and SolidJS - actions: - get-started: Get Started - view-github: View Github - disclaimer: Disclaimer - privacy: Privacy Policy - terms: Terms of Service - -# ! components/WorkInProgress.vue -wip: WIP, please stay tuned - -tooltip: - # Reserved and used by valaxy-theme-press - edit_this_page: Edit this page on GitHub - back_to_top: Back to Top - -post: - # Reserved and used by valaxy-theme-press - contributors: Contributors - -# ! components/OpenListDownload.vue -download: - all: All - os: Operating System - cpu: CPU Architecture - down_source: Download Source - download: Download - gh_proxy: GhProxy - version: Version - beta: Beta - latest: Latest diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml deleted file mode 100644 index a5473820b..000000000 --- a/locales/zh-CN.yml +++ /dev/null @@ -1,76 +0,0 @@ -siteConfig: - title: OpenList 文档 - description: OpenList 文档网站 - -# ! Navigation bar search entry -search: - tip: search - -# ! Navigation menu -nav: - configuration: 配置指南 - guide: 用户指南 - faq: FAQ - seeds: 传输种子 - community: - title: 社区 - discussions: Github 讨论区 - telegram: Telegram - telegram_channel: Telegram 频道 - github: GitHub - ecosystem: 生态项目 -# ! Category titles (setting `title` in index.md's frontmatter will not work) -category: - # User Guide - configuration: 配置指南 - guide: 用户指南 - installation: 安装教程 - advanced: 高级设置 - drivers: 添加存储 - # FAQ - faq: FAQ - seeds: 传输种子 - # Developer Guide - developer: 开发指引 - api: API 文档 - admin: admin - # Changelog - changelog: Changelog - # Ecosystem - ecosystem: 生态项目 - eco_official: 官方生态项目 - eco_worker: OpenList Worker - -# ! Index page (layout: hero) -hero: - banner: 🗂️ 一个支持多种存储的文件列表程序,使用 Gin 和 SolidJS - actions: - get-started: 快速上手 - view-github: 查看 GitHub - disclaimer: 免责声明 - privacy: 隐私政策 - terms: 使用条款 - -# ! components/WorkInProgress.vue -wip: 编写中,敬请期待 - -tooltip: - # Reserved and used by valaxy-theme-press - edit_this_page: 在 GitHub 上编辑此页 - back_to_top: 回到顶部 - -post: - # Reserved and used by valaxy-theme-press - contributors: 贡献者 - -# ! components/OpenListDownload.vue -download: - all: 全部 - os: 操作系统 - cpu: CPU 架构 - download: 下载 - down_source: 下载来源 - gh_proxy: 加速下载 - version: 版本 - beta: 测试版 - latest: 最新版 diff --git a/package.json b/package.json index 1435e7687..11ac3b1a0 100644 --- a/package.json +++ b/package.json @@ -1,53 +1,38 @@ { "name": "openlist-docs", "version": "0.0.0", + "private": true, "type": "module", - "packageManager": "pnpm@10.12.1", + "packageManager": "bun@1.4.2", + "engines": { + "node": ">=24" + }, "license": "AGPL-3.0", "scripts": { - "build": "node utils/ensure-full-git.js && npm run build:ssg", - "build:spa": "valaxy build", - "build:ssg": "NODE_OPTIONS='--max-old-space-size=8192' valaxy build --ssg", - "dev": "valaxy", - "rss": "valaxy rss", - "serve": "vite preview", - "vitepress:dev": "vitepress dev", - "vitepress:build": "vitepress build", - "vitepress:preview": "vitepress preview", + "dev": "vitepress dev", + "build": "node utils/ensure-full-git.js && vitepress build", + "preview": "vitepress preview", "format": "prettier --write .", "format:check": "prettier --check .", "prepare": "husky" }, "dependencies": { "@giscus/vue": "^3.1.1", - "@iconify-json/devicon-plain": "^1.2.31", - "@iconify-json/logos": "^1.2.4", - "@iconify-json/ri": "^1.2.5", - "@iconify-json/vscode-icons": "^1.2.23", - "valaxy": "0.25.9", - "valaxy-addon-algolia": "0.2.0", - "valaxy-addon-components": "0.1.0", - "valaxy-addon-git-log": "0.3.2", - "valaxy-theme-press": "0.25.9", - "vitepress-plugin-group-icons": "^1.6.1" + "gray-matter": "^4.0.3", + "medium-zoom": "^1.1.0", + "mermaid": "^12.0.0", + "vitepress": "^1.6.4", + "vitepress-plugin-group-icons": "^1.7.6", + "vue": "^3.5.43" }, "devDependencies": { - "@iconify-json/carbon": "1.2.9", - "@iconify-json/devicon": "^1.2.32", - "@iconify-json/simple-icons": "1.2.39", - "@types/node": "^24.0.15", + "@types/node": "^24.19.0", "husky": "^9.1.7", - "lint-staged": "^16.1.2", - "nodemon": "^3.1.10", - "prettier": "^3.6.2", - "vite": "6.3.5", - "vitepress": "1.1.4", - "vue": "^3.5.17", - "vue-i18n": "^11.1.10", - "vue-tsc": "^2.2.12" + "lint-staged": "^17.6.0", + "prettier": "^3.9.9" }, "lint-staged": { - "*.{js,ts,vue,md,json,yml,yaml}": [ + "*.{js,ts,mts,vue,md,json,yml,yaml}": [ "prettier --write" ] } diff --git a/pages/api/apidocs.md b/pages/api/apidocs.md index 5f190b52e..cfb5b59cf 100644 --- a/pages/api/apidocs.md +++ b/pages/api/apidocs.md @@ -1,38 +1,14 @@ --- -title: - en: API Documentation - zh-CN: API 文档 -icon: iconfont icon-setting -# This control sidebar order top: 10 -# A page can have multiple categories categories: - api -# A page can have multiple tags -tag: - - API - - Guide -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -Visit [fox.oplist.org](https://fox.oplist.org) to view and interact with our API documentation online. Live debugging is supported. +# API 文档 -All contents, endpoints, and code provided by this API are licensed under the [GNU Affero General Public License v3 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html). - -![apifox](/img/api/apifox-light.png#light) -![apifox](/img/api/apifox-dark.png#dark) - -::: -::: zh-CN 访问 [fox.oplist.org](https://fox.oplist.org) 在线查看API文档并调试 。 所有通过此API提供的内容、端点和代码均根据[GNU Affero通用公共许可证v3(AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html)授权。 ![apifox](/img/api/apifox-light.png#light) ![apifox](/img/api/apifox-dark.png#dark) - -::: diff --git a/pages/configuration/configuration.md b/pages/configuration/configuration.md index eace7dfc3..460ee3de1 100644 --- a/pages/configuration/configuration.md +++ b/pages/configuration/configuration.md @@ -1,30 +1,13 @@ --- -title: - en: Configuration - zh-CN: 配置文件 categories: - configuration -# A page can have multiple tags top: 70 --- -### Initial config { lang="en" } +# 配置文件 -### 初始配置 { lang="zh-CN" } +### 初始配置 -::: en -::: tip -After modifying the configuration file, restart OpenList for changes to take effect - -- Windows/macOS: `/data/config.json` -- Linux: one-click script directory, `/opt/openlist/data/config.json` or `/data/config.json` -- Docker: `/data/config.json` -- OpenWrt: modify config on server if using `luci-app-openlist` , otherwise `/data/config.json` -- Other: `/data/config.json` - -::: - -::: zh-CN ::: tip `config.json` 内配置文件修改后都需要重启 OpenList 才会生效 @@ -173,42 +156,14 @@ After modifying the configuration file, restart OpenList for changes to take eff } ``` -## Field Explanation { lang="en" } - -## 字段说明 { lang="zh-CN" } +## 字段说明 ### force -::: en -By default OpenList reads the configuration from environment variables, set this field to `true` to force OpenList to read config from the configuration file. - -::: - -::: zh-CN 程序会优先从环境变量中读取配置,设置 `force` 为 `true` 会使程序忽略环境变量强制读取配置文件。 -::: - ### site_url -::: en -The address of your OpenList server, such as `https://pan.example.com`. This address is essential for some features, and thus thry may not work properly if unset: - -- thumbnailing `LocalStorage` -- previewing site after setting web proxy -- displaying download address after setting web proxy -- reverse-proxying to site sub directories -- ... - -Do not include the slash \(`/`\) at the end of the address. For example: - -```diff -+ "site_url": "https://openlist.example.com", -- "site_url": "https://openlist.example.com/", -``` - -::: -::: zh-CN 你的网站 URL,比如 `https://pan.example.com`,这个地址会在程序中的某些地方使用,如果不设置这个字段,一些功能可能无法正常工作,比如 - 本地存储的缩略图 @@ -224,26 +179,14 @@ URL 链接结尾请勿携带 `/`,参照如下示例,否则也将无法使用 - "site_url": "https://openlist.example.com/", ``` -::: - ### cdn -::: en -The address of the CDN. Included `$version` values will be dynamically replaced by the version of OpenList. Existing dist resources are hosted on both npm and GitHub, which can be found at: -::: -::: zh-CN CDN 地址,如果要使用 CDN,可以设置该字段,`$version` 会被动态替换为 `Openlist-Frontend` 的实际版本。 -::: - - -::: en -Thus it is possible to use any npm or ~~GitHub~~ CDN path for this field. Do not include the slash `/` at the end of the address. For example: -::: -::: zh-CN 所以你可以使用任何 npm 或 ~~GitHub~~ CDN 作为路径,结尾不要携带 `/`,例如: -::: - `https://registry.npmmirror.com/@openlist-frontend/openlist-frontend/$version/files/dist` - `https://unpkg.com/@openlist-frontend/openlist-frontend@$version/dist` @@ -255,115 +198,14 @@ Thus it is possible to use any npm or ~~GitHub~~ CDN path for this field. Do not - ~~`https://cdn.jsdelivr.net/gh/OpenListTeam/OpenList-Frontend@$version/dist`~~ - ~~`https://jsd.onmicrosoft.cn/gh/OpenListTeam/OpenList-Frontend@$version/dist`~~ -::: en -::: tip -If you are using the lite version, please add `/lite` to the end of the URL. For example: `https://cdn.jsdelivr.net/gh/OpenListTeam/OpenList-Frontend@$version/dist/lite` -::: -::: zh-CN ::: tip 如果您使用 Lite 版本,请在地址后面添加 `/lite` 目录,如:`https://cdn.jsdelivr.net/npm/@openlist-frontend/openlist-frontend@$version/dist/lite`。 ::: -::: en -Keep empty to use dist resources embedded in the program by default. -::: -::: zh-CN 默认情况下,您可以将其设置为空以使用程序内置 dist。 -::: - -#### CDN for Beta version { lang="en" } - -#### Beta 版本使用 CDN { lang="zh-CN" } - -::: en -Since the frontend uses Vite for building, the generated JS files use a hash naming strategy. When code changes occur, the built file names change, making `index.html` files from different versions incompatible with each other. - -The OpenList backend needs to load `index.html` into memory for processing, including inserting code and modifying variables. If deployed only through Pages with modified API addresses, it would result in missing functionality and routing failures. - -To solve this problem, we've added the ability to fetch `index.html` from CDN for Beta versions and self-built versions, ensuring that JS files and `index.html` are properly matched. - -- **Release version**: Static resources are loaded through NPM CDN with fixed versions, so there's no need to request from CDN - the built-in `index.html` can be used directly - - Note: Some NPM CDNs (like npmmirror) may prohibit access to HTML files, but Release versions don't depend on CDN's `index.html`, so they're unaffected -- **Beta version**: Updates frequently, OpenListTeam doesn't upload to NPM, and no CDN is provided for Beta versions - -Since Beta versions don't have NPM CDN provided by OpenListTeam, you need to deploy it yourself: - -1. **Deploy frontend build artifacts** - - Deploy build artifacts to a CDN platform (you can use Cloudflare Pages, EdgeOne Pages, etc.) - - Configure necessary CORS headers, for example of `edgeone.json`: - - ```json - { - "headers": [ - { - "source": "/*", - "headers": [ - { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, - { - "key": "Access-Control-Allow-Methods", - "value": "GET, OPTIONS" - }, - { - "key": "Access-Control-Allow-Headers", - "value": "Content-Type" - } - ] - }, - { - "source": "/**/*.mjs", - "headers": [ - { - "key": "Content-Type", - "value": "application/javascript" - } - ] - } - ] - } - ``` - - - Recommend using "copy + overwrite" deployment method, retaining old version resource files to ensure compatibility for versions not rebooted - -2. **Configure backend** - - Add CDN configuration in `config.json` - - The program will automatically fetch the latest `index.html` from CDN when starting - -3. **Version updates** - - When you need to update the frontend, simply restart the backend program - -Here's a diagram for better understanding: -```mermaid -flowchart TD - A[Program Initialization] --> B{CDN Configured?} - - B -->|Yes| D{Version Type?} - B -->|No| E{Local dist_dir Configured?} - - E -->|No| F[Use Built-in index.html] - E -->|Yes| G[Use index.html from Local dist_dir] - - D -->|Beta Version| H[Fetch index.html from CDN] - D -->|Release Version| F - - H --> I{CDN Fetch Successful?} - I -->|Success| J[Use CDN-fetched index.html] - I -->|Failed| K[Resty Retry 3 Times] - K -->|Success| J - K -->|Still Failed| L[Program Exit, Wait for Restart] +#### Beta 版本使用 CDN - F --> M[Replace CDN, Site Info and Other Variables] - G --> M - J --> M - M --> N[Return Page] -``` - -::: - -::: zh-CN 由于前端采用 Vite 构建,生成的 JS 文件使用哈希命名策略。当代码发生变更时,构建后的文件名会改变,这导致不同版本间的 `index.html` 文件无法互相兼容。 OpenList 后端需要将 `index.html` 加载到内存中进行处理,包括插入代码、修改变量等操作。如果仅通过 Pages 部署并修改 API 地址,会导致功能缺失和路由失效。 @@ -450,53 +292,16 @@ flowchart TD M --> N[返回页面] ``` -::: - ### jwt_secret -::: en -The secret used to sign the JWT token, randomly generated on first run. - -::: -::: zh-CN 用于签署 JWT 令牌的密钥,第一次启动时随机生成。 -::: - ### token_expires_in -::: en -User login expiration time, in hours. -::: -::: zh-CN 用户登录过期时间,单位:小时 -::: ### database -::: en -The database configuration, which is by default `sqlite3`. Available options are `sqlite3`, `mysql` and `postgres`. - -- The database options do not need to be modified if using `sqlite3`. - -```json - "database": { - "type": "sqlite3", //database type - "host": "", //database host - "port": 0, //database port - "user": "", //database account - "password": "", //database password - "name": "", //database name - "db_file": "data\\data.db", //Database location, used by sqlite3 - "table_prefix": "x_", //database table name prefix - "ssl_mode": "", //To control the encryption options during the SSL handshake, the parameters can be searched by themselves, or check the answer from ChatGPT below - "dsn": "" // https://github.com/alist-org/alist/pull/6031 - }, -``` - -::: - -::: zh-CN 数据库配置,默认是 `sqlite3`,也可以使用 `mysql` 或者 `postgres`。 - 如果不使用 `MySQL` 或者 `postgres`,配置文件数据库选项不用修改 @@ -516,30 +321,6 @@ The database configuration, which is by default `sqlite3`. Available options are }, ``` -::: - -::: en -::: details Expand to view details of `ssl_mode` -If you don't know how to fill it in, then your server probably doesn't have SSL enabled, so just leave it blank. -In MySQL, the `ssl_mode` parameter is used to specify the authentication mode of the SSL connection. Here are a few common options: - -- `DISABLED`: Disable SSL connections. -- `PREFERRED`: Use an SSL connection if server has SSL enabled, and otherwise fallback to a normal connection. -- `REQUIRED`: Force to use SSL connection and fail if the server does not support SSL connection. -- `VERIFY_CA`: Force to use SSL connection and verify the authenticity of the server certificate. -- `VERIFY_IDENTITY`: Force to use an SSL connection and verify the authenticity of the server certificate and that the name matches the connecting hostname. - Additional, MySQL 5.x and 8.x have differences. If you are using databases provided by service providers, BTFM. If you deployed the database yourself, STFW. - In PostgreSQL, the `ssl_mode` parameter is used to specify how the client uses SSL connections. Here are a few common options: -- `disable`: Disable SSL connections. -- `allow`: Allow SSL connections. -- `prefer`: Use an SSL connection if server has SSL enabled, and otherwise fallback to a normal connection. -- `require`: Force to use SSL connection and fail if the server does not support SSL connection. -- `verify-ca`: Force to use SSL connection and verify the authenticity of the server certificate. -- `verify-full`: Force to use an SSL connection and verify the authenticity of the server certificate and that the name matches the connecting hostname. - -::: - -::: zh-CN ::: details 展开查看 `ssl_mode` 参数选项 如果不知道如何填,那你的服务器应该也并没有开启SSL,留空即可。 在 MySQL 中,`ssl_mode` 参数是用于指定 SSL 连接的验证模式。以下是几种常见的选项: @@ -564,16 +345,6 @@ In MySQL, the `ssl_mode` parameter is used to specify the authentication mode of ::: -::: en -::: details Notes on modifying the database when there is already data - -1. If you change the `sqlite` database to `mysql` database, it is first recommended to use the backup and recovery method. -2. If you directly import `sqlite` data into `mysql`, you can view this video tutorial: [View tutorial](https://www.bilibili.com/video/BV1iV4y1T7kh) - - Because when directly importing the cloud disk database table, the time of `sqlite` and the time of `mysql` are filled in differently, an error will be reported [please check the precautions and how to solve it](https://www.bilibili.com/video/BV1iV4y1T7kh?t=343.7) - -::: - -::: zh-CN ::: details 已有数据情况下修改数据库注意事项 1. 如果将`sqlite`数据库改为`mysql`数据库优先推荐使用备份再恢复的方法 @@ -584,23 +355,6 @@ In MySQL, the `ssl_mode` parameter is used to specify the authentication mode of ### meilisearch -::: en - -```json - "meilisearch": { - "host": "http://localhost:7700", // meilisearch host, the default is the local machine - "api_key": "", // if meilisearch's authentication is enabled, this is required - "index": "" // meilisearch index uid - }, -``` - -- Documentation link: -- Reference Links: - -::: - -::: zh-CN - ```json "meilisearch": { "host": "http://localhost:7700", // meilisearch主机,默认使用的是本机 @@ -612,32 +366,8 @@ In MySQL, the `ssl_mode` parameter is used to specify the authentication mode of - 文档链接: - 参考链接: -::: - ### scheme -::: en -The configuration of scheme. Set this field if using HTTPS. - -- Remember to copy the certificate file to the data directory. Config example: - -```json - "scheme": { - "address": "0.0.0.0", // The http/https address to listen on, default `0.0.0.0` - "http_port": 5244, // The http port to listen on, default `5244`, if you want to disable http, set it to `-1` - "https_port": -1, // The https port to listen on, default `-1`, if you want to enable https, set it to non `-1` - "force_https": false, // Whether the HTTPS protocol is forcibly, if it is set to True, the user can only access the website through HTTPS - "cert_file": "data\\cert.crt", // Path of cert file - "key_file": "data\\key.key", // Path of key file - "unix_file": "", // Unix socket file path to listen on, default empty, if you want to use unix socket, set it to non empty - "unix_file_perm": "", // Unix socket file permission, set to the appropriate permissions - "enable_h2c": false // Support HTTP/2 Cleartext (H2C) protocol for openlist's http service. The cleartext HTTP/2 protocol supports nginx's grpc_pass after it is enabled - https://github.com/AlistGo/alist/pull/8294 - }, -``` - -::: - -::: zh-CN 协议配置,如果要使用 HTTPS,可以设置该字段。 - 填写示例:记得把证书文件丢到 data 目录里面才会识别到喔~ @@ -656,49 +386,20 @@ The configuration of scheme. Set this field if using HTTPS. }, ``` -::: - ### temp_dir -::: en -The directory to keep temporary files. By default OpenList uses `data/temp`. -::: -::: zh-CN 用于存放临时文件的目录。默认情况下,OpenList 使用 `data/temp`。 -::: - -::: en -::: danger -temp_dir is a temporary folder exclusive to alist. In order to prevent OpenList from generating garbage files when being interrupted, the directory will be cleared every time OpenList starts, so do not store anything in this directory or map this directory & subdirectories to directories in use when using Docker. -::: -::: zh-CN ::: danger temp_dir 为 OpenList 独占的临时文件夹,为避免程序中断产生垃圾文件会在每次启动时清空,故请不要手动在此文件夹内放置任何内容,也不要在使用 docker 时将此文件夹及其子文件夹映射至正在使用的文件夹。 ::: ### bleve_dir -::: en -Where data is stored when using **`bleve`** index. -::: -::: zh-CN 你使用 **`bleve`** 索引时,数据存放的位置 -::: ### dist_dir -::: en -If this option is set, the front-end files in the defined **local** external folder will be used as a priority. - -- Supports using front-end files from an external folder -- Supports using other front-end files, while the back-end continues to use the original version of the application - -Upload the front-end files (dist) to the application's `data` folder, then fill in: - -::: - -::: zh-CN 如果设置此项,优先使用定义的**本地**外部文件夹下的前端文件。 - 支持使用外部文件夹中的前端文件 @@ -706,62 +407,12 @@ Upload the front-end files (dist) to the application's `data` folder, then fill 将前端文件(dist)上传到应用的 `data` 文件夹下,然后填写: -::: - ```json "dist_dir": "data/dist", ``` ### log -::: en -The log configuration. Set this field to save detailed logs of disable. - -```json - "log": { - "enable": true, // Whether OpenList should store logs - "name": "data\\log\\log.log", // The path and name of the log file - "max_size": 10, // the maximum size of a single log file, in MB. After reaching the specified size, the file will be automatically split. - "max_backups": 5, // the number of log backups to keep. Old backups will be deleted automatically when the limit is exceeded. - "max_age": 28, // The maximum number of days preserved in the log file, the log file that exceeds the number of days will be deleted - "compress": false, // Whether to enable log file compression functions. After compression, the file size can be reduced, but you need to decompress when viewing, and the default is to close the state false - "filter": { // skip some logs output, not enable by default - "enable": false, - "filters": [ // preset example - { - "cidr": "", - "path": "/ping", // Health check - "method": "" - }, - { - "cidr": "", - "path": "", - "method": "HEAD" // HEAD request - }, - { // WebDav metadata - "cidr": "", - "path": "/dav/", - "method": "PROPFIND" - } - ] - }, -``` - -Each filter acts as the following object: - -```json -{ - "cidr": "", - "path": "", // http path, If it starts with "/", it is an absolute path; if it does not start with "/", it is a relative path - "method": "" // HTTP/webdav method, in uppercase -} -``` - -Take note of the startup log to confirm the load, as detailed in the source code `server/middlewares/filtered_logger.go`. - -::: - -::: zh-CN 日志配置,如果要查看详细日志(或禁用它),可以设置该字段。 ```json @@ -807,24 +458,8 @@ Take note of the startup log to confirm the load, as detailed in the source code 注意查看启动日志以确认加载情况,具体实现详见源代码 `server/middlewares/filtered_logger.go`. -::: - ### delayed_start -::: en - -Whether to delay OpenList startup. - - - -**Time unit: second** - -Generally this option is used when OpenList is configured to auto-start. The reason is that sometimes network takes some time to connect, so drivers requiring cannot start correctly after OpenList starts. - -::: - -::: zh-CN - 是否延时启动,一般此功能常用于 OpenList 开机自启动选项。 @@ -833,126 +468,27 @@ Generally this option is used when OpenList is configured to auto-start. The rea 因为有时候网络连接的慢,导致 OpenList 启动过快后需要网络连接的驱动无法连接导致无法正常打开。 -::: - ### max_connections -::: en -The maximum amount of connections at the same time. The default is 0, which is unlimited. - -- 10 or 20 is recommended for general devices such as N1(S905D). -- Usage Scenarios: the device will crash if the device is bad at concurrency when picture mode is enabled. - -::: - -::: zh-CN 同时连接的最大数量。默认值为 0,表示无限制。 - 对于性能一般的设备,如 N1(S905D),推荐设置为 10 或 20。 - 使用场景:当启用图片模式时,如果设备的并发能力较差,设备可能会崩溃。 -::: - ### max_concurrency -::: en -Limit the maximum concurrency of local agents. The default value is 64, and 0 means no limit. -::: - -::: zh-CN 限制本地代理的最大并发,默认为64,0为不限制。 -::: ### tls_insecure_skip_verify -::: en -Whether not to verify the SSL certificate. - -If there is a problem with the certificate of the website used when this option is not enabled (such as not including the intermediate certificate, having the certificate expired, or forging the certificate, etc.), the service will not be available. - -When this option is enabled, please ensure the program is running in a safe network environment. -::: - -::: zh-CN 是否不验证 SSL 证书。 如果在未启用此选项时使用的网站证书存在问题(如未包含中间证书、证书过期或证书伪造等),服务将无法使用。 启用此选项时,请确保在安全的网络环境中运行程序。 -::: ### tasks -::: en -Configuration for background task threads. - -```json - "tasks": { - "download": { - "workers": 5, - "max_retry": 1, - "task_persistant": false - }, - "transfer": { - "workers": 5, - "max_retry": 2, - "task_persistant": false - }, - "upload": { - "workers": 5, - "max_retry": 0, - "task_persistant": false - }, - "copy": { - "workers": 5, - "max_retry": 2, - "task_persistant": false - }, - "decompress": { - "workers": 5, - "max_retry": 2, - "task_persistant": false - }, - "decompress_upload": { - "workers": 5, - "max_retry": 2, - "task_persistant": false - }, - "allow_retry_canceled": false - }, -``` - -- **workers**: Number of task threads. -- **max_retry**: Number of retries. - - 0: Retries disabled. -- **download**: Download task when downloading offline -- **transfer**: upload transfer task after offline download is completed -- **upload**: upload task -- **copy**: copy the task -- **decompress**:decompress the task -- **decompress_upload**:decompress upload the task -- **task_persistant**:The task is persistent and will not be cancelled after restarting `OpenList` - - **download**:false - - **transfer**:false - - **upload**:false - - **copy**:false - - **decompress**:false - - **decompress_upload**:false -- **allow_retry_canceled**:Allow users to retry previously canceled tasks - ---- - -A new **transmission** configuration path is added to the background configuration: `/@manage/settings/traffic` - -- Supports limiting the number of threads and transmission uplink and downlink rates of **6 tasks** -- **** - Operation principle: If `settings/traffic` does not have a thread number field (first run or just upgraded from an old version), `settings/traffic` will be initialized with the value of the config configuration file. If `settings/traffic` has a value, the thread configuration information of config will be ignored -- **** -- Summary: For newly installed or upgraded versions, the values will be read from the configuration file to initialize the `traffic` configuration information. Subsequent modifications to the thread only need to be modified in the background. - -::: - -::: zh-CN 后台任务线程数量配置。 ```json @@ -1019,35 +555,8 @@ A new **transmission** configuration path is added to the background configurati - **** - 总结:新安装或者新升级的版本,会先从配置文件读取数值来初始化 `传输` 配置信息,后续修改线程只需要在后台修改就可以 -::: - ### cors -::: en -Configuration for Cross-Origin Resource Sharing (CORS). - -```json - "cors": { - "allow_origins": [ - "*" - ], - "allow_methods": [ - "*" - ], - "allow_headers": [ - "*" - ] - } -``` - -- **allow_origins**: Allowed sources. -- **allow_methods**: Allowed request methods. -- **allow_headers**: Allowed request headers. - -Use it to understand it by yourself, and then configure it. If you do n’t know, please do n’t modify it at will. Use the default configuration. -::: - -::: zh-CN **跨源资源共享**配置 ```json @@ -1069,30 +578,9 @@ Use it to understand it by yourself, and then configure it. If you do n’t know - **allow_headers**:允许的请求头 具体使用方式自行了解进行配置,如果不了解请勿随意修改,使用默认配置就可以。 -::: ### S3 -::: en - -```json - "s3": { - "enable": false, - "port": 5246, - "ssl": false - } -``` - -- `enable`:Whether the S3 function is enabled, the default is not enabled -- `port`:port -- `SSL`:Enable the HTTPS certificate, not enabled by default - -Function introduction: [Click to view](../guide/advanced/s3.md) - -::: - -::: zh-CN - ```json "s3": { "enable": false, @@ -1107,43 +595,8 @@ Function introduction: [Click to view](../guide/advanced/s3.md) 功能介绍:[点击查看](../guide/advanced/s3.md) -::: - ### ftp -::: en - -```json - "ftp": { - "enable": false, - "listen": ":5221", - "find_pasv_port_attempts": 50, - "active_transfer_port_non_20": false, - "idle_timeout": 900, - "connection_timeout": 30, - "disable_active_mode": false, - "default_transfer_binary": false, - "enable_active_conn_ip_check": true, - "enable_pasv_conn_ip_check": true - }, -``` - -- `enable`: Whether the **ftp** function is enabled, not enabled by default -- `listen`: port number -- `find_pasv_port_attempts`: maximum number of attempts to re-find a port due to port conflicts during passive transmission -- `active_transfer_port_non_20`: enable ports other than 20 as active transmission ports -- `idle_timeout`: maximum idle time (seconds) when there is no client request -- `connection_timeout`: connection timeout -- `disable_active_mode`: disable active transmission mode -- `default_transfer_binary`: transfer in binary mode by default -- `enable_active_conn_ip_check`: perform IP check on the client side of the TCP connection of the data stream in active transmission mode -- `enable_pasv_conn_ip_check`: perform IP check on the client side of the TCP connection of the data stream in passive transmission mode - -Other instructions: [Click to view](../guide/advanced/ftp.md) -::: - -::: zh-CN - ```json "ftp": { "enable": false, @@ -1171,27 +624,9 @@ Other instructions: [Click to view](../guide/advanced/ftp.md) - `enable_pasv_conn_ip_check`:被动传输模式下对数据流TCP连接的客户端进行IP检查 其它说明:[点击查看](../guide/advanced/ftp.md) -::: ### sftp -::: en - -```json - "sftp": { - "enable": false, - "listen": ":5222" - } -``` - -- `enable`: Whether the **sftp** function is enabled, not enabled by default -- `listen`: port number - -Other instructions: [Click to view](../guide/advanced/ftp.md) -::: - -::: zh-CN - ```json "sftp": { "enable": false, @@ -1203,25 +638,9 @@ Other instructions: [Click to view](../guide/advanced/ftp.md) - `listen`:端口号 其它说明:[点击查看](../guide/advanced/ftp.md) -::: ### mcp -::: en - -```json - "mcp": { - "enable": false - } -``` - -- `enable`: Whether the **MCP** endpoint is enabled, not enabled by default - -Other instructions: [Click to view](../guide/advanced/mcp.md) -::: - -::: zh-CN - ```json "mcp": { "enable": false @@ -1231,24 +650,9 @@ Other instructions: [Click to view](../guide/advanced/mcp.md) - `enable`:**MCP** 功能是否启用,默认未启用 其它说明:[点击查看](../guide/advanced/mcp.md) -::: ### proxy -::: en - -```json - "proxy_address": "", -``` - -Supports HTTP proxy, HTTPS proxy, SOCKS4 proxy, SOCKS5 proxy, SOCKS5HOSTNAME proxy. - -Both IPv4 and IPv6 are supported. - -::: - -::: zh-CN - ```json "proxy_address": "", ``` @@ -1256,5 +660,3 @@ Both IPv4 and IPv6 are supported. 支持HTTP代理、HTTPS代理、SOCKS4代理、SOCKS5代理、SOCKS5HOSTNAME代理。 同时兼容IPv4和IPv6。 - -::: diff --git a/pages/configuration/global.md b/pages/configuration/global.md index 4f20eba27..c48d3509b 100644 --- a/pages/configuration/global.md +++ b/pages/configuration/global.md @@ -1,31 +1,13 @@ --- -title: - en: Global Configuration - zh-CN: 全局设置 -# This is the icon of the page -icon: iconfont icon-chrome -# This control sidebar order -# A page can have multiple categories categories: - configuration -# A page can have multiple tags top: 20 --- -## Hide files { lang="en" } +# 全局设置 -## 隐藏文件 { lang="zh-CN" } +## 隐藏文件 -::: en -Match files hidden by a regular expression (`JavaScript`). Please do not fill it in casually, as incorrect expressions may cause the front-end page to crash. - -One regular expression per line. - -By default, there is an expression `/\/README.md/i`, which hides `README.md` from all directories. - -Note that this is not a true hide. The file will still appear in the API response list, but will not be displayed in the front-end interface. If you wish to truly hide the file, please refer to [metadata](../guide/advanced/meta.md). -::: -::: zh-CN 匹配由正则表达式(`JavaScript`)隐藏的文件。请勿随意填写,错误的表达式可能会导致前端页面崩溃。 每行填写一个正则表达式。 @@ -33,95 +15,34 @@ Note that this is not a true hide. The file will still appear in the API respons 默认情况下,已有一个表达式 `/\/README.md/i`,表示将 `README.md` 从所有目录中隐藏。 需要注意的是,这并不是真正的隐藏。该文件仍会出现在 API 返回的文件列表中,只是不显示在前端界面中。如果您想彻底隐藏文件,请参考[元信息](../guide/advanced/meta.md)。 -::: - -## Package download { lang="en" } - -## 打包下载 { lang="zh-CN" } -::: en -Whether to enable package download (default is true). **Not recommended, especially for large or numerous files.** +## 打包下载 -- It is recommended to push files to Aria2 for downloading, as it supports maintaining the directory structure when saving the download folder. -- For more details, see [What is the difference between the two Aria2](./other.md#other). - -::: -::: zh-CN 是否开启前端文件打包下载(默认为开启)。**不推荐使用,尤其是文件数量多或体积大的情况。** - 推荐将文件推送至 Aria2 进行下载,它支持在保存下载文件夹时保留目录结构。 - 了解详情,请查看 [两个 Aria2 的区别](./other.md#其他)。 -::: - -## Customize head { lang="en" } - -## 自定义头部 { lang="zh-CN" } - -::: en -The content set here will be automatically inserted into the header of the webpage (not include the manage pages). You can reference scripts, CSS, etc., here to style the frontend of OpenList. - -- How to configure PWA(Desktop、Android、IOS):**[alist/issues/6724](https://github.com/alist-org/alist/issues/6724#issuecomment-2220251541)** +## 自定义头部 -::: -::: zh-CN 在此处设置的内容会自动插入到网页头部位置(管理页面除外)。您可以在此处引用脚本、CSS等,对 OpenList 的前端进行美化。 - 如何配置PWA(Desktop、Android、IOS):**[alist/issues/6724](https://github.com/alist-org/alist/issues/6724#issuecomment-2220251541)** -::: - -## Customize body { lang="en" } - -## 自定义内容 { lang="zh-CN" } +## 自定义内容 -::: en -The content set here will be automatically inserted at the end of the webpage body. You can add icp information, visit statistics, etc., here. -::: -::: zh-CN 在此处设置的内容会自动插入到网页正文的末尾。您可以在此处添加备案信息、访问统计等。 -::: - -## Link expiration { lang="en" } -## 直链有效期 { lang="zh-CN" } +## 直链有效期 -::: en -The expiration time of the direct link, in hours. If it equals 0, it will not expire. Default is 0. -::: en -::: warning -Only the straight chain of the path with the password added will have an expiration time, otherwise it will not expire.Because the expiration time is added to the sign query parameter, and the path without adding the password will not check the sign. -::: - -::: zh-CN 直接链接的过期时间,以小时为单位。如果为 0,则不会过期。默认值为 0。 -::: zh-CN + ::: warning 只有加了密码的路径的直链才会有过期时间,否则不会过期。因为过期时间是加到sign查询参数中的,没有加密码的路径是不会检查sign的。 ::: -## Sign all { lang="en" } - -## 签名所有 { lang="zh-CN" } - -::: en -Add signatures to the direct link of all files (whether with password or not), such as `https://xxxx.com/d/xx?sign=vUQ5KFXnwMseKnIUXGRcfoG3cEHzKFBiPGp1NriMDXA=:0`. +## 签名所有 -If you need to close it, you can close it yourself, but you need to pay attention to security issues. After closing the signature, if the site can be accessed by the public network, the password may be bypassed to access private files. -There are two other methods that also carry the `sign?xxx` parameter: - -1. Add Storage Select `Enable Signing` -2. Meta Information Add Password - -The scope of the three methods: `Sign All` > `Meta Information Add Password` > Add Storage Select `Enable Signing` - -1. Sign All: If this option is turned on, the sign parameter will be carried regardless of whether meta-information is encrypted or not, and whether `Enable Signing` is checked when adding storage. -2. Meta Information Add Password:Only files under this meta information path will carry the sign parameter. - - If **Apply to sub folder** is turned on, all files in this path will carry the sign parameter -3. Add Storage Select `Enable Signing`:Only this storage driver carries the sign parameter. - -::: -::: zh-CN 向所有文件的直接链接添加签名(无论是否有密码),即 `https://openlist.example.com/d/xx?sign=vUQ5KFXnwMseKnIUXGRcfoG3cEHzKFBiPGp1NriMDXA=:0`。 若需要关闭,自行关闭即可,但需要注意安全问题。关闭签名后若站点能被公网访问,可能会被绕过密码访问私人文件。 @@ -138,112 +59,36 @@ The scope of the three methods: `Sign All` > `Meta Information Add Password` > A - 如果**应用到子文件夹**开启,则该路径下的所有文件将携带该签名参数 3. 添加存储勾选`启用签名`:单独只这个存储驱动携带 sign 参数 -::: - -## Privacy regs { lang="en" } +## 隐私内容正则表达式 -## 隐私内容正则表达式 { lang="zh-CN" } - -::: en -What you don't want to show in the error message, One regular expression (in `Golang`) per line. The matched content will be replaced with `*` of the corresponding length. -::: -::: zh-CN 不想在错误消息中显示的内容,每行一个正则表达式(在 `Golang` 中)。匹配的内容将被替换为对应长度的`*`。 -::: - -## Ocr api { lang="en" } - -## Ocr 接口 { lang="zh-CN" } - -::: en -Used to identify verification codes. You can deploy yourself: https://hub.docker.com/r/cloudlinksu/openlist-ocr-server. -The default ocr api is deployed on the [Hugging Face](https://huggingface.co/spaces/Susus21/openlist-ocr/tree/main). You can clone the Hugging Face repository to build your own: [clone the Hugging Face repository](https://huggingface.co/spaces/Susus21/openlist-ocr/tree/main?duplicate=true). +## Ocr 接口 -The hf domain name after successful self-construction is `https://{username}-{repositroy-name}.hf.space/ocr/file/json`. -::: -::: zh-CN 用于识别验证码。你可以自己部署:https://hub.docker.com/r/cloudlinksu/openlist-ocr-server。 默认的 ocr api 部署在 [Hugging Face](https://huggingface.co/spaces/Susus21/openlist-ocr/tree/main) 上。你可克隆 Hugging Face 仓库自建:[克隆Hugging Face仓库](https://huggingface.co/spaces/Susus21/openlist-ocr/tree/main?duplicate=true)。 自建成功后的 hf 域名为:`https://{username}-{repositroy-name}.hf.space/ocr/file/json` -::: -## Filename char mapping { lang="en" } +## 文件名字符映射 -## 文件名字符映射 { lang="zh-CN" } - -::: en -Map certain special characters, such as `/`, which is used as a path separator in OpenList. When file names contain `/`, it may cause issues like broken file paths or inability to view the files. By using this method, we can map and convert these characters to resolve such problems. -::: -::: zh-CN 映射一些特殊符号,例如 /,在 OpenList 中作为路径符号。由于某些文件名包含 /,可能会导致文件路径断开或无法查看等问题。通过这种方式,我们可以进行符号映射和转换,以解决此类问题。 -::: ```json { "/": "|", "xx1": "xx1", "xx2": "xx2" } ``` -## Forward direct link params { lang="en" } - -## 转发直链参数 { lang="zh-CN" } +## 转发直链参数 -::: en -After enabling, the parameters after `?` will be automatically appended to the end of the direct link URL. -::: -::: zh-CN 开启后,`?`后面的参数将自动添加到直链 URL 的末尾。 -::: - -## Ignore direct link params { lang="en" } -## 忽略直链参数 { lang="zh-CN" } +## 忽略直链参数 -::: en -Ignore the parameters for forwarding direct links, such as `sign` and `openlist_ts`. -::: -::: zh-CN 忽略转发直链的参数,如 `sign,openlist_ts`。 -::: - -## Webauthn login enabled { lang="en" } - -## 启用 Webauthn 登录 { lang="zh-CN" } -::: en -**Web Authentication (WebAuthn)** is a new authentication method. You can enable it by following these steps: +## 启用 Webauthn 登录 -1. **Enable WebAuthn Feature**: Go to the admin panel, navigate to `Settings` → `Global`, and enable the option `Enable WebAuthn Login`. -2. **Bind WebAuthn Credentials**: Go to the `Profile` page in the admin panel and click `Add WebAuthn Credential` to bind your credentials. - - Supported authentication methods: - - Local PIN code - - Companion devices (e.g., smart bands, smartwatches) - - Windows Hello options (e.g., facial recognition, fingerprint recognition) - -3. **Log in with WebAuthn**: After binding, you can log in using WebAuthn: - - On the login page, click the login button on the far right. - - Enter your username. - - Click login. - - Follow the prompts to complete WebAuthn verification (unlock the respective authentication method). - -::: tip WebAuthn only supports secure origins. - -**Supported environments:** - -- Websites with `https://` protocol -- Localhost environment (`localhost`) - -**Unsupported environments:** - -- `http://` protocol -- Local network IPs (e.g., `192.168.x.x`) -- Local IPs (e.g., `127.0.0.1`) -- Direct access via server IP - -::: -::: zh-CN **Web Authentication (WebAuthn)** 是一套新的身份验证方法。您可以按照以下步骤启用: 1. **启用 WebAuthn 功能**:进入后台`设置` → `全局`,开启`启用 Webauthn 登录`选项 @@ -276,76 +121,24 @@ Ignore the parameters for forwarding direct links, such as `sign` and `openlist_ ::: -## Allow previewing sharing files { lang="en" } - -## 允许预览分享文件 { lang="zh-CN" } +## 允许预览分享文件 -::: en -Enable the preview feature for files in share links. - -Please note that disabling this option will only hide preview methods (except Download) on the frontend, but cannot prevent users from invoking the relevant APIs. -::: -::: zh-CN 允许分享链接中文件的预览功能。 请注意:关闭该选项只会让前端不显示除 Download 以外的预览方式,并不能阻止用户调用相关 API。 -::: -## Allow previewing sharing archives { lang="en" } +## 允许预览分享的压缩文件 -## 允许预览分享的压缩文件 { lang="zh-CN" } - -::: en -Enable the preview feature for archives in share links. - -Unlike the previous setting, disabling this option will prevent calls to the archive preview APIs for files in share links. -::: -::: zh-CN 允许分享链接中压缩文件的预览功能。 与上一项配置不同,关闭该选项会拦截对分享链接中压缩文件的预览相关请求。 -::: -## Force proxy sharing files { lang="en" } +## 强制代理分享文件链接 -## 强制代理分享文件链接 { lang="zh-CN" } - -::: en -Enforce proxying for all file requests originating from share links. -::: -::: zh-CN 强制代理所有来自分享链接的文件请求 -::: - -## Share summary content { lang="en" } - -## 分享链接复制内容 { lang="zh-CN" } - -::: en -The content copied by clicking "Copy Link" after sharing completion, coding in Handlebars template syntax. - -If you prefer copying the accessible link directly, try: - -- Preview link - -```handlebars -{{base_url}}/@s/{{id}} -``` - -- Preview link with share code -```handlebars -{{base_url}}/@s/{{id}}{{#if pwd}}?pwd={{pwd}}{{/if}} -``` +## 分享链接复制内容 -- direct download link (applies only to single-file shares) - -```handlebars -{{base_url}}/sd/{{id}}{{#if pwd}}?pwd={{pwd}}{{/if}} -``` - -::: -::: zh-CN 分享完成后点击“复制链接”复制的内容,使用 Handlebars 模板语法,默认内容的中文版: ```handlebars @@ -386,45 +179,18 @@ If you prefer copying the accessible link directly, try: {{base_url}}/sd/{{id}}{{#if pwd}}?pwd={{pwd}}{{/if}} ``` -::: - -## Handle hook after writing { lang="en" } - -## 写入操作后触发目录更新钩子 { lang="zh-CN" } - -::: en -Whether to trigger the directory update hook after operations such as upload, rename, delete, move, copy, or extraction. +## 写入操作后触发目录更新钩子 -Triggering the directory update hook will cause the index to update and activate the function of the [Strm](/guide/drivers/strm) driver to generate local files. -::: -::: zh-CN 上传、重命名、删除、移动、复制、解压操作后,是否触发目录更新钩子。 目录更新钩子触发将导致索引更新及[Strm](/guide/drivers/strm)驱动生成本地文件的功能触发。 -::: - -## Handle hook rate limit { lang="en" } -## 目录更新钩子遍历限制速率 { lang="zh-CN" } +## 目录更新钩子遍历限制速率 -::: en -It is only meaningful when the [Handle hook after writing](/configuration/global#handle-hook-after-writing) is enabled. When the directory update hook is triggered, this limits the rate at which the driver API is called (unit: times/second; 0 means no restriction). -::: -::: zh-CN 仅当[写入操作后触发目录更新钩子](/configuration/global#写入操作后触发目录更新钩子)开启时有意义,触发目录更新钩子时,限制调用驱动 API 的速率(单位:次/秒,为 0 表示不限制)。 -::: - -## Ignore system files { lang="en" } -## 忽略系统文件 { lang="zh-CN" } +## 忽略系统文件 -::: en -When enabled, attempts by users to upload certain system files will directly fail, thereby achieving filtering. - -The determination of whether a file is a system file is based on its filename. -::: -::: zh-CN 开启时,当用户尝试上传某些系统文件时会直接失败,从而实现过滤。 判断是否为系统文件的依据为文件名。 -::: diff --git a/pages/configuration/index.md b/pages/configuration/index.md index dd6b8e017..2ca782814 100644 --- a/pages/configuration/index.md +++ b/pages/configuration/index.md @@ -1,31 +1,11 @@ ---- -title: - en: Configuration - zh-CN: 配置 ---- +# 配置 --- -::: en - -- [Configuration file](./configuration/configuration) -- [Site settings](./configuration/site) -- [Style settings](./configuration/style) -- [Preview settings](./configuration/preview) -- [Global settings](./configuration/global) -- [Other settings](./configuration/other) -- [Side settings](./configuration/side) - -::: - -::: zh-CN - -- [配置文件](./configuration/configuration) -- [站点设置](./configuration/site) -- [样式设置](./configuration/style) -- [预览设置](./configuration/preview) -- [全局设置](./configuration/global) -- [其他设置](./configuration/other) -- [侧边栏设置](./configuration/side) - -::: +- [配置文件](./configuration) +- [站点设置](./site) +- [样式设置](./style) +- [预览设置](./preview) +- [全局设置](./global) +- [其他设置](./other) +- [侧边栏设置](./side) diff --git a/pages/configuration/other.md b/pages/configuration/other.md index e7a5c0844..959e013c7 100644 --- a/pages/configuration/other.md +++ b/pages/configuration/other.md @@ -1,82 +1,34 @@ --- -title: - en: Other Configuration - zh-CN: 其他设置 -# This is the icon of the page -icon: iconfont icon-snow categories: - configuration -# A page can have multiple tags top: 10 --- -## Aria2 - -::: en -Used for the offline download feature, which downloads an external resource outside of OpenList into the storage managed by OpenList. -Set Aria2 uri and Aria2 for offline download. +# 其他设置 -> `Aria2` needs to access the same directory as `OpenList`: `/opt/openlist/data/temp/aria2`. Assuming that both Aria2 and OpenList are independent containers, where OpenList mounts the directory `/etc/openlist/data:/opt/openlist/data`, Aria2 containers need to be additionally mounted under the path `/etc/openlist/data/temp/aria2:/opt/openlist/data/temp/aria2`. +## Aria2 -::: -::: zh-CN 这是用于离线下载功能,即:将一个OpenList外部的资源下载到OpenList所管理的存储中。 设置 Aria2 URI 以供离线下载。 > **`Aria2` 需要和`OpenList`访问同一目录: `/opt/openlist/data/temp/aria2`。假设您的Aria2和OpenList都是独立容器,其中OpenList挂载的目录为`/etc/openlist/data:/opt/openlist/data`,则Aria2容器需要额外挂载的`/etc/openlist/data/temp/aria2:/opt/openlist/data/temp/aria2`路径下。** -::: - -### Aria2 地址 { lang="zh-CN" } - -### Aria2 uri { lang="en" } +### Aria2 地址 -::: en -The Aria2 RPC address used for offline downloading. The default value is: `http://localhost:6800/jsonrpc`. -::: -::: zh-CN 用于离线下载的 Aria2 RPC 地址。默认值为:`http://localhost:6800/jsonrpc`。 -::: -### Aria2 秘钥 { lang="zh-CN" } +### Aria2 秘钥 -### Aria2 secret { lang="en" } - -::: en -The Aria2 RPC secret key used for offline downloading. The default value is empty. -::: -::: zh-CN 用于离线下载的 Aria2 RPC 秘钥。默认值为空。 -::: ## qBittorrent -::: en -Used to customize **qBittorrent** parameters to configure the client to use. - -The default value is: `http://admin:adminadmin@localhost:8080/`, you can modify it by referring to [specific instructions](../guide/advanced/offline-download.md#qbittorrent) -::: -::: zh-CN 用于自定义 **qBittorrent** 参数用来配置客户端中使用。 预设值为:`http://admin:adminadmin@localhost:8080/`,您可以参考 [具体说明](../guide/advanced/offline-download.md#qbittorrent) 进行修改。 -::: - -## 115、PikPak、Thunder { lang="en" } - -## 115、PikPak、迅雷 { lang="zh-CN" } - -::: en -**You need to add the driver first, and then set the temporary directory in the settings.** -Allow the use of offline download tools such as 115/PikPak/Thunder in any storage. +## 115、PikPak、迅雷 -- Files will be downloaded directly to the destination dir if using the tool in 115/PikPak/Thunder storage. -- Otherwise, files will be downloaded to a user-configured temp dir, and then transfered to the destination dir. - - For example, on the front-end page of the `GoogleDrive` storage drive, when the `Pikpak offline-download` function is invoked, the file will first be downloaded to the Pikpak temporary folder directory set in the backend. Once the Pikpak offline download is complete, the file will be automatically transferred from Pikpak to `GoogleDrive`. - -::: -::: zh-CN **需要先添加对应的驱动,然后在后台设置临时目录。** 允许在任意存储使用 115/PikPak/迅雷 等离线下载工具。 @@ -85,36 +37,11 @@ Allow the use of offline download tools such as 115/PikPak/Thunder in any storag - 否则,文件会下载至用户配置的临时目录中,然后转移至目标目录。 - 例如,在 `GoogleDrive` 存储驱动的前端页面中,调用 `Pikpak offline-download` 功能时,文件会先下载到后台设置的 Pikpak 临时文件夹目录。待 Pikpak 完成离线下载后,文件会自动从 Pikpak 转移到 `GoogleDrive`。 -::: - -### Token { lang="en" } - -## 令牌 { lang="zh-CN" } +## 令牌 -::: en -The token that can be used to access all APIs of the program. Unlike the token obtained after logging in with a username and password, this token is fixed and has no expiration time. -::: -::: zh-CN 可用于访问程序所有 API 的令牌。与账号密码登录后获取的不同,此令牌一般固定,且没有失效时间。 -::: - -### Other { lang="en" } - -### 其他 { lang="zh-CN" } -::: en - -1. When using OpenList, you may notice two Aria2 options. What is the difference between them? [**Click here for detailed explanation**](../faq/why.md#what-is-the-difference-between-the-two-aria2) - -2. Supports using Aria2 to download folders while preserving the original directory structure. - - **Configure Aria2**: Go to the bottom-right corner, click `Settings` → `Aria2 RPC Link` → Enter the `Aria2 RPC Key` (if available). - - Aria2 will download files locally, so you only need to initiate the download on your local machine. It also supports pushing the download task to another computer, your own server, or other devices in your local network, as long as the target device has Aria2 installed and is connected to either the public internet or the local network. - - - **Start downloading**: Check the `Enable checkbox` in the bottom-right corner → Select the files/folders → Click `Download` at the bottom → `Send to Aria2`. - - **Important notes**: It is recommended not to download too many files at once, such as thousands of folders or tens of thousands of files, as this may cause performance issues. - -::: -::: zh-CN +### 其他 1. 在使用时发现有两个 Aria2,那它们有什么区别呢?[**点击查看详情说明**](../faq/why.md#两个aria2有什么不同) 2. 支持使用 Aria2 下载文件夹并保持原有目录结构 @@ -123,5 +50,3 @@ The token that can be used to access all APIs of the program. Unlike the token o - **开启下载**:右下角勾选 `开启复选框` → 选择文件/文件夹 → 点击底部 `下载` → `发送到 Aria2` - **注意事项**:建议不要一次性下载过多文件,如同时下载上千个文件夹或成千上万个文件,以免导致性能问题。 - -::: diff --git a/pages/configuration/preview.md b/pages/configuration/preview.md index 4bc7f0816..a06b4e037 100644 --- a/pages/configuration/preview.md +++ b/pages/configuration/preview.md @@ -1,136 +1,39 @@ --- -title: - en: Preview Configuration - zh-CN: 预览设置 -# This is the icon of the page -icon: iconfont icon-preview -# This control sidebar order categories: - configuration -# A page can have multiple tags top: 30 --- -## Text types { lang="en" } +# 预览设置 -## 文本类型 { lang="zh-CN" } +## 文本类型 -::: en -The extensions of the files you want to preview as text, split by `,`, such as `txt,md,go,tsx`. -::: -::: zh-CN 要作为文本预览的文件扩展名,用 `,` 分隔,例如 `txt,md,go,tsx`。 -::: -## Audio types { lang="en" } +## 音频类型 -## 音频类型 { lang="zh-CN" } - -::: en -The extensions of the files you want to preview as audio, split by `,`, such as `mp3,wav,m4a`. -::: -::: zh-CN 要作为音频预览的文件扩展名,以 `,` 分隔,例如 `mp3,wav,m4a`。 -::: - -## Video types { lang="en" } -## 视频类型 { lang="zh-CN" } +## 视频类型 -::: en -The extensions of the files you want to preview as video, split by `,`, such as `mp4,webm,ogg`. -::: -::: zh-CN 要作为视频预览的文件扩展名,以 `,` 分隔,例如 `mp4,webm,ogg`。 -::: - -## Image types { lang="en" } -## 图片类型 { lang="zh-CN" } +## 图片类型 -::: en -The extensions of the files you want to preview as image, split by `,`, such as `jpg,jpeg,png,gif,webp`. -::: -::: zh-CN 要作为图像预览的文件扩展名,以 `,` 分隔,例如 `jpg,jpeg,png,gif,webp`。 -::: -### Proxy types { lang="en" } +## 代理类型 -## 代理类型 { lang="zh-CN" } - -::: en -The file extensions to be downloaded through the program proxy, split by `,`, such as `m3u8,url`. -::: -::: zh-CN 要通过程序代理下载的文件扩展名,以 `,` 分隔,例如 `m3u8,url`。 -::: -### Proxy ignore headers { lang="en" } +## 代理忽略头部 -## 代理忽略头部 { lang="zh-CN" } - -::: en -Do not forward request headers, such as `authorization,referer`, when downloading through the program proxy. - -This can prevent download failures caused by header parameter leakage. -::: -::: zh-CN 当通过程序代理下载时不转发的请求标头,例如 `authorization,referer`。 可以避免请求头参数泄露导致下载失败。 -::: - -### External previews { lang="en" } - -## 外部预览 { lang="zh-CN" } - -::: en -A json object that contains the external preview settings, It is defined as - -```typescript -interface { - [extensions: string]: { - [name: string]: string //(url) - } -} -``` - -the first key `extensions` is the file extensions separated by `,` (if it starts with `/`, it will be considered a regular expression), and the value is also a `key-value` object, the key is the preview name, and the value is the external url. - -For the external URL, we provide some variables for you to use: - -Basic variable: - -- `$url`: the URL of file, such as `https://openlist.example.com/p/file.pdf`. -- `$durl`: the direct URL of file, such as `https://oss.example.com/cloud/user/2020/01/01/file.pdf`. -- `$name`: the file name - -Extended variables: -Add [eb_] before the basic variable, where e means `encodeURIComponent`, b means `base64`, such as: - -- `$e_url`: encodeURIComponent($url) -- `$b_url`: btoa($url) -- `$eb_url`: encodeURIComponent(btoa($url)) - -Finally, the `External previews` will displayed a `Open with` menu while current file matched the `extensions`. -For example, set `External previews` to -```json -{ - "txt": { - "Notepad": "notepad://$url" - }, - "/.*/": { - "VSCode": "vscode://$url" - } -} -``` +## 外部预览 -when we enter a file with the extension `txt`, it will show: -![Open-with](/img/config/open-with.png) -::: -::: zh-CN 一个包含外部预览设置的 json 对象,定义为 ```typescript @@ -174,36 +77,9 @@ interface { 当我们输入一个扩展名为 `txt` 的文件时,它会显示: ![Open-with](/img/config/open-with.png) -::: - -### Iframe previews { lang="en" } - -## Iframe 预览 { lang="zh-CN" } - -::: en -Similar to `External previews`, but it will embed an iframe in current page directly. - -You need to enter a preview HTML page, and then pass the file address as a parameter to the page using a variable. The available variables are listed in the previous section. -:::tip -If you want to use self-deployed OnlyOffice to preview office files, you can add the configuration like this here: - -```json -{ - "doc,docx,xls,xlsx,ppt,pptx": { - "OnlyOffice": "you_only_office_url" - } -} -``` - -Deploying and using `OnlyOffice` Reference: - -- https://github.com/AlistGo/alist/discussions/3255 -- https://github.com/AlistGo/alist/discussions/8271 - -::: +## Iframe 预览 -::: zh-CN 类似于 `外部预览`,但它会直接在当前页面中嵌入 iframe。 您需要填入一个预览的 HTML 网页,然后将文件地址通过变量作为参数传给网页。可以使用的变量见上节。 @@ -227,82 +103,34 @@ Deploying and using `OnlyOffice` Reference: ::: -### Audio cover { lang="en" } - -## 音频封面 { lang="zh-CN" } +## 音频封面 -::: en -The default audio cover. -::: -::: zh-CN 歌曲无播放封面时显示的默认封面。 -::: -### Audio autoplay { lang="en" } +## 自动播放音频 -## 自动播放音频 { lang="zh-CN" } - -::: en -Whether to automatically play audio files. -::: -::: zh-CN 是否自动播放音频文件。 -::: -### Video autoplay { lang="en" } +## 自动播放视频 -## 自动播放视频 { lang="zh-CN" } - -::: en -Whether to automatically play video files. -::: -::: zh-CN 是否自动播放视频文件。 -::: - -### Preview archives by default { lang="en" } -## 默认情况下预览档案 { lang="zh-CN" } +## 默认情况下预览档案 -::: en -After enabling this option, compressed files will be previewed by default (as shown in the image below). Although it will consume some server bandwidth, a full download will not occur. If you wish to disable the preview for compressed files, turning off this option will change the default behavior to download mode. -::: -::: zh-CN 启用此选项后,默认会对压缩包格式的文件进行预览(如下图所示)。虽然会消耗一些服务器流量,但不会进行完整下载。如果您希望关闭压缩包格式的预览,关闭此选项后,默认行为将改为下载模式。 -::: ![](/img/advanced/user_read_archives_light.png#light) ![](/img/advanced/user_read_archives_dark.png#dark) -### Readme autorender { lang="en" } - -## Readme 自动渲染 { lang="zh-CN" } +## Readme 自动渲染 -::: en -After closing, the content of files like readme.md will no longer be automatically rendered. - -By default, `readme.md`, `footer.md`, and `bottom.md` will be rendered at the bottom of the file, while `header.md`, `top.md`, and `index.md` will be rendered at the top of the file. -::: -::: zh-CN 关闭后,`readme.md` 等文件的内容将不会被自动渲染。 默认情况下,`readme.md`、`footer.md` 和 `bottom.md` 会渲染在文件底部,而 `header.md`、`top.md` 和 `index.md` 会渲染在文件顶部。 -::: - -### Filter readme scripts { lang="en" } -## 过滤 Readme 文件中的脚本 { lang="zh-CN" } - -::: en -Prevent virus script attacks. After turning it on, the contents of `readme.md` will be displayed in text form. - -- Including but not limited to strikethrough, tables, task lists, etc. displayed in text form -- The specific effects are as follows: - ![](/img/config/readme_b.png#light) +## 过滤 Readme 文件中的脚本 -::: -::: zh-CN 防止病毒脚本攻击,开启后会以文本形式显示 `readme.md` 内容。 - 包含但不限于 删除线、表格、任务列表等以文本形式展示 @@ -310,29 +138,8 @@ Prevent virus script attacks. After turning it on, the contents of `readme.md` w ![](/img/config/readme_b.png#light) ![](/img/config/readme_h.png#dark) -::: - -### Force preview { lang="en" } - -## 强制预览 { lang="zh-CN" } - -::: en -We can force the preview type through the `type` query parameter. - -Available values: - -``` -UNKNOWN -FOLDER -VIDEO -AUDIO -TEXT -IMAGE -``` +## 强制预览 -Example: `http://yourdomain/test-file.ahk?type=text` -::: -::: zh-CN 可以通过 `type` 请求参数来强制设置预览类型。 可选值: @@ -347,19 +154,9 @@ IMAGE ``` 示例: `http://yourdomain/test-file.ahk?type=text` -::: - -## Specify preview { lang="en" } -## 指定预览 { lang="zh-CN" } +## 指定预览 -::: en -We can specify the preview through the `preview` query parameter. The parameter will be generated when you select a preview in the file page. - -Example: `http://yourdomain/test-file.ahk?preview=download` -::: -::: zh-CN 可以通过 `preview` 请求参数来指定预览的 `key`。此参数会在文件页面选择预览时生成。 示例: `http://yourdomain/test-file.ahk?preview=download` -::: diff --git a/pages/configuration/side.md b/pages/configuration/side.md index ed62b9ed9..97b1c3c62 100644 --- a/pages/configuration/side.md +++ b/pages/configuration/side.md @@ -1,72 +1,11 @@ --- -title: - en: Side Configuration - zh-CN: 侧边栏设置 -# This is the icon of the page -icon: fa-regular fa-sidebar-flip categories: - configuration -# A page can have multiple tags top: 0 --- -::: en -We can see a icon on the lower right corner of the homepage. If your computer resolution is very high, the icon is likely to be smaller -After the expand +# 侧边栏设置 -- - Refresh - - The refresh we mentioned is not to use F5 to refresh the webpage, **but use this icon to refresh**, so that the files that just uploaded to the network disk can be refreshed out. - -- - New File - -- - New Folder - -- - Recursive Move - - Move all the files in the current directory and all files in the subfolder to the designated folder - - :warning: Dangerous operation, please use it with caution. - -- - Remove Empty Folder - -- - Batch Rename - -- - Upload - - Open the upload modal. - -- - Offline download - - Open the offline download modal. - -- - Toggle Outline - - Toggle the outline for markdown viewer. - -- - Toggle Checkbox - - Select multiple files. - - In the display of Toggle Checkbox, first trigger the selection operation to prevent jumping into a deeper folder - - In the display of Toggle Checkbox, change it to hold down the `Alt` key and click to open the folder (macOS Press and hold the `Option` key) - -- - Local Settings - - Open the local settings. - -::: - -::: zh-CN 我们在主页右下角能看到一个 图标,如果你电脑分辨率很高 可能图标比较小 展开后分别是 @@ -120,43 +59,11 @@ After the expand 打开本地设置页面。 -::: - -::: en -::: tip -If you didn't find this feature, please update the version -::: - -::: zh-CN ::: tip 如果你没有找到此功能,请更新版本 ::: -## Upload Modal { lang="en" } - -## 上传窗口 { lang="zh-CN" } - -::: en - -![](/img/config/side/upload-modal-en.png) - -Drag files to the field to upload, or click `Choose Folder` or `Choose File` to select files to upload. - -There are two methods to upload files: - -1. **Stream**: directly puts the binary content of the file into the body, so that the backend can use very little memory, it seem to have no downsides. - -2. **Form**: is to wrap the file into formdata and upload it. The memory used by the backend will be more, but there is no limit on the size. - -There are the following options: - -- **Add as Task**: Create an upload task in the background. -- **Overwrite existing File**: If the file already exists, it will be overwritten. -- **Try rapid**: Upload the file with its checksum information, requires cloud storage support. - -::: - -::: zh-CN +## 上传窗口 ![](/img/config/side/upload-modal-zh.png) @@ -175,31 +82,7 @@ There are the following options: - **尝试秒传**:在上传时携带文件的校验信息,需要网盘支持 -::: - -## Offline Download Modal { lang="en" } - -## 离线下载窗口 { lang="zh-CN" } - -::: en - -![](/img/config/side/offline-download-modal-en.png) - -Download the file offline and upload it to the network disk directory you designated. - -If you want to keep the offline downloaded file, please add the link first, select the options below, and then start the offline download. - -**How to use**: Navigate to the target directory, then select "Offline Download" from the bottom-right corner. - -Supported offline download tools: `Aria2`, `qBittorrent`, `SimpleHttp`. - -**Note**: When using the offline download feature, OpenList must be installed and running on the corresponding device. Offline downloads cannot be performed across different devices. - -For more information, please refer to [Offline Download](../guide/advanced/offline-download.md). - -::: - -::: zh-CN +## 离线下载窗口 ![](/img/config/side/offline-download-modal-zh.png) @@ -215,53 +98,7 @@ For more information, please refer to [Offline Download](../guide/advanced/offli 详见:[离线下载](../guide/advanced/offline-download.md)。 -::: - -## Local Settings Modal { lang="en" } - -## 本地设置窗口 { lang="zh-CN" } - -::: en - -Local settings will be saved to the browser's local storage. - -1. **Aria2 RPC URL** and **Aria2 RPC secret**: This field is for entering the user's Aria2 information, which enables batch downloading on the client side, not on the server side. - -2. **Global default layout**: The selection of view selection in the upper right corner is to enter that folder to modify it only for the only folder. This setting can consistently take effect without using a folder setting. - -3. **Show folder in image view**: literal meaning - -4. **Show sidebar**: Add a list directory on the left side of the list like Windows Explorer, which can jump to a deep folder faster - -5. **Show file count message**: At the bottom of the page (in list view) or at the top (in grid or image view), display the total number of items in the current folder and how many items are selected. - -6. **Position of header & nav bar**: Is the logo and navigation bar displaying at the drop -down list? - - **Normal**: No need to display - - **Stick to top of page**: adsorb LOGO and navigation bar - - **Only nav bar sticky**: Do not adsorb LOGO, only adsorb navigation bar - -7. **Grid item size** : Adjust the picture size in the Grid item mode. Default: 90. - -8. **List item filename overflow**: When the text is too long, the three state selection - - **Ellipsis**: Default - - **Scrollable**: PC users use the `Shift` + `mousewheel` on the file, if it is a mobile phone user sliding the file left and right - - **Multi-line** - -9. **Open item on Checkbox**: - - **Direct**: Open the file/enter the folder - - **Double Click** - - **Disable while checked** - - With `Ctrl` or `Command` (macOS) hold - - With `Alt` or `Option` (macOS) hold - - If the button option does not take effect, please check whether the browser plugin conflict - - Tips: You can press the `Shift` key to select one, and then select one end. You can quickly select all the files in the middle - -10. **Language package selection** and **night mode and daytime mode switch** - -::: - -::: zh-CN +## 本地设置窗口 本地设置会被保存到浏览器的 Local Storage 中。 @@ -298,5 +135,3 @@ Local settings will be saved to the browser's local storage. Tips:可以按着 `Shift` 键选择一个开头,再选择一个结尾可以快速选择中间的全部 10. **语言包选择** 和 **夜间白天切换** - -::: diff --git a/pages/configuration/site.md b/pages/configuration/site.md index 1ebf2a6b8..30dc0f35d 100644 --- a/pages/configuration/site.md +++ b/pages/configuration/site.md @@ -1,58 +1,26 @@ --- -title: - en: Site Configuration - zh-CN: 网站设置 -# This is the icon of the page -icon: iconfont icon-tab -# A page can have multiple categories categories: - configuration -# A page can have multiple tags top: 50 --- -## Version { lang="en" } +# 网站设置 -## 版本 { lang="zh-CN" } +## 版本 -::: en -The version of OpenList, readonly. -::: -::: zh-CN OpenList 的版本,只读。 -::: -## Site title { lang="en" } +## 网站标题 -## 网站标题 { lang="zh-CN" } - -::: en -The title of OpenList, such as `OpenList`. -::: -::: zh-CN OpenList 的标题,例如 OpenList。 -::: - -## Announcement { lang="en" } - -## 公告 { lang="zh-CN" } - -::: en -The announcement of OpenList, such as `### Hello\nWelcome to use OpenList`. We suggest you add a title in front of the content, such as `### Hello`, so that the content will not be covered by `Close Button`. - -- If you don't want to display it, you can clear the contents of the announcement to not display it. -- If you think the `x` button in the upper right corner is in the way, you can use the following CSS to remove it -::: +## 公告 -::: zh-CN OpenList 的公告,如 `### Hello\nWelcome to use OpenList`。我们建议您在内容前添加标题,例如 `### Hello`,以免内容被关闭按钮覆盖。 - 如想不显示,可以清空公告内内容即可不显示。 - 如果嫌右上角的 `x` 按钮碍事可以使用如下CSS去掉 -::: - ```css ``` -## Pagination type { lang="en" } - -## 分页类型 { lang="zh-CN" } - -::: en - -- All: All files will be displayed at once. -- Pagination: Show a `Paginator` at the bottom of the page. -- Load more: Show a `Load more` button at the bottom of the page. -- Auto load more: Automatically load more files when scrolling to the bottom of the page. - -::: -::: zh-CN +## 分页类型 - 全部:一次显示所有文件。 - 分页:在页面底部显示一个“分页器”。 - 加载更多:在页面底部显示“加载更多”按钮。 - 自动加载更多:滚动到页面底部时自动加载更多文件。 -::: - -## Default page size { lang="en" } +## 默认每页文件数量 -## 默认每页文件数量 { lang="zh-CN" } - -::: en -The default page size of the `openlist` if `Pagination type` doesn't set to `All`, such as `20`. -::: -::: zh-CN OpenList 的默认每页文件数量,当 `Pagination type` 没有被设置为 `全部` 时生效,例如 `20`。 -::: - -## Allow indexing { lang="en" } -## 允许索引 { lang="zh-CN" } +## 允许索引 -::: en -Whether to allow others to mount your OpenList to build the index, check it and enable it. - -The default is off, use with caution. (New features in version 3.8.0) -::: - -::: zh-CN 是否允许其他人挂载你的OpenList后进行索引构建,勾选后开启。 **默认为关闭状态,谨慎使用。** (v3.8.0 新增功能) -::: - -## Allow mounted { lang="en" } - -## 允许挂载 { lang="zh-CN" } -::: en -Whether to allow being mounted by other OpenList instances. +## 允许挂载 -**The default is enabled. If you don't want to be mounted by others, it's recommended to disable this option.** (New feature in v3.16.3) -::: - -::: zh-CN 是否允许被其它 OpenList 挂载。 **默认为开启状态,如果你不想被别人挂载建议关闭。**(v3.16.3新增功能) -::: ## robots.txt -::: en -Crawler configuration/rules. - -The default is to allow crawlers to access all pages. - -`Allow: /` Indicates that the crawlers of search engines are allowed to visit all pages: - -```txt{2} -User-agent: * -Allow: / -``` - -If you want to prevent crawlers from accessing all pages, you can change it to this: - -```txt{2} -User-agent: * -Disallow: / -``` - -::: - -::: zh-CN 爬虫的配置/规则。 默认允许爬虫访问所有页面。 @@ -166,5 +71,3 @@ Allow: / User-agent: * Disallow: / ``` - -::: diff --git a/pages/configuration/style.md b/pages/configuration/style.md index a48ae879b..63b85a166 100644 --- a/pages/configuration/style.md +++ b/pages/configuration/style.md @@ -1,98 +1,39 @@ --- -title: - en: Style Configuration - zh-CN: 样式设置 -# This is the icon of the page -icon: iconfont icon-style -# This control sidebar order categories: - configuration -# A page can have multiple tags top: 40 --- -## Logo +# 样式设置 -::: en -The logo is displayed in the upper left corner of the page. The default is the `OpenList logo`. +## Logo -You can set two different ones at night and during the day, one link per line. -::: -::: zh-CN Logo 显示在页面的左上角。默认为 OpenList Logo。 可以分别设置夜间和白天两个不同的,方法一行一个链接。 -::: -## Favicon { lang="en" } +## 网站图标 -## 网站图标 { lang="zh-CN" } - -::: en -The favicon is displayed in the browser tab. The default is the `OpenList logo`. -::: -::: zh-CN 网站图标(Favicon)显示在浏览器选项卡中。默认为 OpenList Logo。 -::: - -## Main color { lang="en" } -## 主颜色 { lang="zh-CN" } +## 主颜色 -::: en -The main color is used to on the page. The default is `#1890ff`. -::: -::: zh-CN 全站页面的主要颜色。默认值为 `#1890ff`。 -::: -## Home icon { lang="en" } +## 首页图标 -## 首页图标 { lang="zh-CN" } - -::: en -The home icon is displayed in the left of the navigation bar. The default is `🏠`. -::: -::: zh-CN 主页图标显示在导航栏的左侧。 默认是 `🏠`。 -::: - -## Home container { lang="en" } - -## 主容器 { lang="zh-CN" } - -::: en -- Max 980px: the main width will set to `min(99%, 980px)` -- Hope container: sets the max-width of the content to match the min-width of the current breakpoint, refer to https://openlistteam.github.io/hope-ui/docs/layout/container - -::: - -::: zh-CN +## 主容器 - Max 980px:主宽度将设置为 `min(99%, 980px)` - Hope 容器:设置内容的最大宽度以匹配当前断点的最小宽度,参考 https://openlistteam.github.io/hope-ui/docs/layout/container -::: - -## Settings layout { lang="en" } - -## 设置布局 { lang="zh-CN" } - -::: en -The layout of the settings items on the management page. - -- Responsive: grids next to each other -- List style: one grid per row - -The default is: List +## 设置布局 -::: -::: zh-CN 管理页面中设置项的布局方式。 - 响应式:一个格子一个格子挨在一起的 - 列表:一行一个格子 默认为:列表 -::: diff --git a/pages/ecosystem/index.md b/pages/ecosystem/index.md index 97453e744..1d2fd8e1c 100644 --- a/pages/ecosystem/index.md +++ b/pages/ecosystem/index.md @@ -1,343 +1,172 @@ --- -title: - en: OpenList Ecosystem - zh-CN: OpenList 生态项目 categories: - ecosystem top: 1000 --- -::: en -This page shows the OpenList ecosystem, including official projects and third-party projects -::: +# OpenList 生态项目 -::: zh-CN 本页面展示了OpenList的生态项目,包括官方项目和第三方项目 -::: -## Official Projects { lang="en" } +## 官方项目 -## 官方项目 { lang="zh-CN" } +### [OpenList Docs ↗](./official_docs) -### [OpenList Docs ↗](./ecosystem/official_docs) - -::: en -Documentation based on Valaxy, supporting multiple styles and real-time builds from the GitHub repository. -::: -::: zh-CN 基于Valaxy的文档,支持多种样式,基于GitHub仓库实时构建。 -::: --- -### [OpenList Desktop ↗](./ecosystem/official_desktop) +### [OpenList Desktop ↗](./official_desktop) -::: en -A cross-platform GUI management interface integrating OpenList and rclone, offering graphical configuration, monitoring, and service management features. -::: -::: zh-CN 整合了OpenList和rclone的跨平台GUI管理界面,提供图形化配置界面、监控和服务管理等实用功能。 -::: --- -### [OpenList Mobile ↗](./ecosystem/official_mobile) +### [OpenList Mobile ↗](./official_mobile) -::: en -A mobile file server application based on OpenList, built with Flutter framework. Turn your phone into a file server. -::: -::: zh-CN 基于OpenList的移动端文件服务器应用,使用Flutter框架开发。将您的手机变成文件服务器。 -::: --- -### [OpenList APIPages ↗](./ecosystem/official_APIpage) +### [OpenList APIPages ↗](./official_APIpage) -::: en -An open-source implementation of the online API functionality in OpenList, deployable on various servers, providing stable and privacy-protected API authentication services for cloud storage users. -::: -::: zh-CN OpenList中在线API功能的开源实现,可灵活部署至多种服务器,可以为网盘用户提供稳定且有双向隐私保障的API鉴权服务。 -::: --- -### [OpenList Proxy ↗](./ecosystem/official_proxy) +### [OpenList Proxy ↗](./official_proxy) -::: en -OpenList traffic proxy program, which can use another server to proxy OpenList traffic. -::: -::: zh-CN OpenList流量代理程序,可以利用另一台服务器代理OpenList的流量 -::: -### [OpenList Worker ↗](./ecosystem/official_worker) +### [OpenList Worker ↗](./official_worker/) -:::: en -The official TypeScript + Serverless port of OpenList running on Cloudflare Workers / EdgeOne / Alibaba Cloud ESA, reusing the official React 19 frontend. -:::: -:::: zh-CN OpenList 官方的 TypeScript + Serverless 移植版,运行于 Cloudflare Workers / EdgeOne / 阿里云 ESA,复用官方 React 19 前端。 -:::: --- -## Third-Party Open Source Projects { lang="en" } - -## 第三方开源项目 { lang="zh-CN" } +## 第三方开源项目 -:::: en -::: tip -Third-party open source projects are based on community open-source security. The following projects are not affiliated with OpenListTeam. Please evaluate the associated risks before deploying them to a production environment. - -If you have a new project that is not listed here, you can submit it through [Issues](https://github.com/OpenListTeam/OpenList-Docs/issues) or [Pull Request](https://github.com/OpenListTeam/OpenList-Docs/pulls). -::: -:::: - -:::: zh-CN ::: tip 以下第三方开源项目均由社区开发并保持开源,与 OpenListTeam 无直接关联。在部署到生产环境前,请自行评估相关风险。 如果您有新的项目没有在此列出,可以通过 [Issues](https://github.com/OpenListTeam/OpenList-Docs/issues) 或 [Pull Request](https://github.com/OpenListTeam/OpenList-Docs/pulls) 提交。 -:::: +::: ### [sbwml/luci-app-openlist ↗](https://github.com/sbwml/luci-app-openlist) -::: en -Provides LuCI support for OpenList, suitable for OpenWRT-based router systems. - -Free and Open Source Software licensed under the MIT license. -::: -:::: - -::: zh-CN 为 OpenList 提供 LuCI 支持,适用于基于 OpenWRT 的路由器系统。 自由开源软件,MIT 协议 -::: --- ### [giturass/openlist_termux ↗](https://github.com/giturass/openlist_termux/) -::: en -OpenList management script for Termux, used for convenient installation, updating, and management of OpenList in the Android Termux environment. - -Free and Open Source Software licensed under the MIT license. -::: - -::: zh-CN Termux 下的 OpenList 管理脚本,用于在 Android Termux 环境中便捷安装、更新和管理 OpenList. 自由开源软件,MIT协议 -::: --- ### [ypq123456789/openlist-manger ↗](https://github.com/ypq123456789/openlist-manger) -::: en -A cross-platform interactive Shell script for managing OpenList, supporting Linux, macOS, Windows (WSL), and Android Termux. - -Free and Open Source Software licensed under the MIT license. -::: - -::: zh-CN 一个用于管理 OpenList 的跨平台交互式 Shell 脚本,支持 Linux、macOS、Windows (WSL) 和 Android Termux。 自由开源软件,MIT协议 -::: --- ### [Alien-Et/OpenList-Magisk ↗](https://github.com/Alien-Et/OpenList-Magisk) -::: en -The OpenList Magisk module integrates the OpenList file server into the Android system, running in a systemized manner through Magisk, supporting ARM and ARM64 architectures. - -Free and Open Source Software licensed under the MIT license. -::: - -::: zh-CN OpenList Magisk 模块将 OpenList 文件服务器集成到 Android 系统中,通过 Magisk 以系统化方式运行,支持 ARM 和 ARM64 架构。 自由开源软件,MIT 协议 -::: --- ### [qianye216/OpenList-Desktop ↗](https://github.com/qianye216/OpenList-Desktop) -::: en -OpenList Desktop is a cross-platform desktop client designed for OpenList (Alist) and Rclone. It provides a modern and user-friendly graphical interface that helps you easily manage Alist services and Rclone cloud storage mounts without needing to memorize and input tedious command-line instructions. - -Free and Open Source Software licensed under the GPLv3 license. -::: - -::: zh-CN OpenList Desktop 是一个为 OpenList (Alist) 和 Rclone 设计的跨平台桌面客户端。它提供了一个现代化且用户友好的图形界面,帮助您轻松管理 Alist 服务和 Rclone 云盘挂载,无需记忆和输入繁琐的命令行指令。 自由开源软件,GPLv3 协议 -::: --- ### [LeoHaoVIP/AListLiteAndroid ↗](https://github.com/LeoHaoVIP/AListLiteAndroid) -::: en -AListLite is an Android application that supports running OpenList services. Compared to the tutorial described in BV1ex4y157xJ, users can use OpenList services on Android devices (phones/TVs) without needing to install Termux. - -Free and Open Source Software licensed under the AGPLv3 license. -::: - -::: zh-CN AListLite 是一款支持运行 OpenList 服务的 Android 应用,相对于教程 BV1ex4y157xJ 中的描述,用户无需借助安装 Termux 即可实现在 Android 设备(手机/电视)上使用 OpenList 服务。 自由开源软件,AGPLv3 协议 -::: --- ### [outlook84/mpv-handler-openlist ↗](https://github.com/outlook84/mpv-handler-openlist) -::: en -This is a URL protocol handler (mpv://) designed for mpv or mpv.net media players on Windows platform. This tool is used to invoke mpv or mpv.net players from OpenList web pages to open video links. - -Free and Open Source Software licensed under the GPLv2 license. -::: - -::: zh-CN 这是一个为 Windows 平台上的 mpv 或 mpv.net 媒体播放器设计的 URL 协议注册器 (mpv://)。该工具用于在 OpenList Web 网页上调用 mpv 或 mpv.net 播放器来打开视频链接。 自由开源软件,GPLv2 协议 -::: --- ### [outlook84/openlist-tvbox-gateway ↗](https://github.com/outlook84/openlist-tvbox-gateway) -::: en -OpenList gateway for TVBox, used to connect OpenList resources to TVBox-compatible clients. - -Free and Open Source Software licensed under the AGPLv3 license. -::: - -::: zh-CN 适用于 TVBox 的 OpenList 网关,用于将 OpenList 资源接入兼容 TVBox 的客户端。 自由开源软件,AGPLv3 协议 -::: --- ### [moyanj/AList3SDK ↗](https://github.com/moyanj/AList3SDK) -::: en -A high-performance Python SDK for easy interaction with OpenList/AList services. Supports both asynchronous and synchronous APIs, and provides comprehensive type annotations! - -Free and Open Source Software licensed under the LGPL-2.1 license. -::: - -::: zh-CN 一个高性能的 Python SDK,用于与 OpenList/AList 服务轻松交互。同时支持异步和同步 API,并提供完善的类型注解! 自由开源软件,LGPL-2.1协议 -::: --- ### [huancun/Openlist-EX-container ↗](https://github.com/huancun/Openlist-EX-container) -::: en -This project is an improved version based on Alist-EX-container. -Integrates Openlist, Aria2, qBittorrent, Caddy reverse proxy, supports multi-arch one-click deployment, navigation page and multiple web panels, perfect for offline download and resource management. - -Free and Open Source Software licensed under the GPLv3 license. -::: - -::: zh-CN 该项目基于 Alist-EX-container 改进。 集成 Openlist、Aria2、qBittorrent、Caddy 反向代理,支持多架构一键部署,带导航页和多种 Web 面板,适合离线下载和资源管理。 自由开源软件,GPLv3 协议 -::: --- ### [hienao/ostrm ↗](https://github.com/hienao/ostrm) -::: en -A full-stack application for converting OpenList file lists into STRM streaming media files. (Originally the openlist-strm project) - -Free and Open Source Software licensed under the GPLv3 license. -::: - -::: zh-CN 一个用于将 OpenList 文件列表转换为 STRM 流媒体文件的全栈应用(原为 openlist-strm 项目) 自由开源软件,GPLv3协议 -::: --- ### [li-peifeng/iNoi ↗](https://github.com/li-peifeng/iNoi) -::: en -An open-source fork of OpenList focusing on stability. - -Free and Open Source Software licensed under the AGPLv3 license. -::: - -::: zh-CN 一个 OpenList 的开源分支,以追求稳定为主 自由开源软件,AGPLv3协议 -::: --- ### [907739769/OpenList-strm-RuoYi ↗](https://github.com/907739769/OpenList-strm-RuoYi/) -::: en -An improved version of OpenList Strm based on RuoYi framework. - -Free and Open Source Software licensed under the MIT license. -::: - -::: zh-CN 基于RuoYi框架升级的OpenList Strm 应用 自由开源软件,MIT协议 -::: --- ### [Liki4/qnap-openlist-webdav ↗](https://github.com/Liki4/qnap-openlist-webdav/) -::: en -QNAP OpenList | The third-party open-source QPKG version of OpenList. - -Free and Open Source Software licensed under the AGPLv3 license. -::: - -::: zh-CN QNAP OpenList | OpenList的第三方开源qpkg版本。 自由开源软件,AGPLv3协议 -::: --- ### [HisAtri/openlistapi ↗](https://github.com/HisAtri/openlistapi/) -::: en -An open-source Python client implementing the OpenList API. - -Free and Open Source Software licensed under the MIT license. -::: - -::: zh-CN 实现了 OpenList API 的 Python 客户端。 自由开源软件,MIT协议 @@ -346,21 +175,12 @@ Free and Open Source Software licensed under the MIT license. ### [jiwangyihao/olist-cdn-preheat ↗](https://github.com/jiwangyihao/olist-cdn-preheat/) -::: en -OpenList dedicated CDN preheating tool. When used with EdgeOne/ESA origin-following redirection, it efficiently improves cache hit rates and accelerates site downloads. - -Free and Open Source Software licensed under the MPLv2 license. -::: - -::: zh-CN OpenList 专用 CDN 预热工具。搭配 EdgeOne/ESA 回源跟随重定向,高效提升缓存命中率,加速站点下载。 自由开源软件,MPLv2协议 -::: ### [MY QNAP's OpenList ↗](https://www.myqnap.org/product/openlist/) -::: zh-CN QNAP存储库 MyQNAP 将 OpenList 应用进行分发并收取 6 美元费用。 我方已与 QNAP 法国团队取得联系,并根据 AGPL 协议及 CC 协议,要求对方提供应用的哈希值,或将该应用下架。 目前,沟通进展如下: @@ -371,33 +191,6 @@ QNAP存储库 MyQNAP 将 OpenList 应用进行分发并收取 6 美元费用。 4. 基于上述情况,QNAP 已临时下架该应用,并将更新页面。展示哈希值后,OpenList 的下载与分发将恢复。 5. 截至目前,QNAP 暂未实际违反协议。我方为防范开源供应链风险而启动的调查行动已完成。 -::: -::: en -QNAP's MyQNAP repository distributes the OpenList application for a fee of $6. -We have contacted the QNAP France team and, based on the AGPL and CC licenses, requested them to provide the hash values of the application or take it down. -The communication progress is as follows: - -1. The application is provided by QNAP officially, not uploaded by users. -2. The charging behavior is not for profit, so the CC license does not apply. -3. According to the AGPL license, if the application has not been modified, hash values like MD5, SHA, etc. must be provided. Currently, QNAP states that the relevant work is in progress. -4. Based on the above situation, QNAP has temporarily taken down the application and will update the page. Once the hash values are displayed, the download and distribution of OpenList will resume. -5. As of now, QNAP has not actually violated the license. Our investigation initiated to prevent open-source supply chain risks has been completed. - -::: - -:::: en -::: details History information (outdated,archived at 2026-01-22) -The third-party closed-source QPKG version of OpenList. - -[Archived on 2025-7-13](https://web.archive.org/web/20250713005135/https://www.myqnap.org/product/openlist/) - -Proprietary software, paid license. - -Note: The source code is not open, violating the AGPLv3 license and the OpenList logo's CC BY-NC-SA 4.0 license. -::: -:::: - -:::: zh-CN ::: details 历史信息(已过时,归档于2026-01-22) OpenList的第三方闭源qpkg版本。 @@ -406,42 +199,18 @@ OpenList的第三方闭源qpkg版本。 专有软件,付费授权 注意:因没有开放源码,违反AGPLv3许可证、违反OpenList LOGO的CC BY-NC-SA 4.0许可。 -:::: - -## Third-Party Proprietary Software { lang="en" } - -## 其他第三方项目 { lang="zh-CN" } - -:::: en -::: danger -The following lists several other third-party projects derived from OpenList that do not fall into the above categories. - -Any actions of these software/projects (including references to OpenList in their names, functionality design, content, or promotional methods) do not represent the stance of the OpenList project. The functionality, consequences of use, and related legal responsibilities are entirely borne by their respective developers or publishers, and the OpenList project assumes no responsibility. ::: -:::: -:::: zh-CN +## 其他第三方项目 + ::: danger 以下列出若干基于 OpenList 衍生的其他不符合上述分类的的第三方项目。 以下软件/项目的任何行为(包括其名称中对 OpenList 的引用、功能设计、内容及宣传方式)均**不代表** OpenList 项目的立场。软件功能、使用后果及相关法律责任完全由其开发者或发布方承担,OpenList 项目概不负责。 -:::: +::: ### [Yxiguan/OpenList_123](https://github.com/Yxiguan/OpenList_123) -:::: en -::: danger -The third-party closed-source modified version of OpenList. - -[Archived on 2025-10-19](https://web.archive.org/web/20251019150201/https://github.com/Yxiguan/OpenList_123) - -Proprietary software, no license. - -Note: The source code is not open, violating the AGPLv3 license. -::: -:::: - -:::: zh-CN ::: danger OpenList的第三方闭源修改版本。 @@ -450,4 +219,4 @@ OpenList的第三方闭源修改版本。 专有软件,无授权 注意:因没有开放源码,违反AGPLv3许可证。 -:::: +::: diff --git a/pages/ecosystem/official_APIpage.md b/pages/ecosystem/official_APIpage.md index ed3fffbdd..842dd36b4 100644 --- a/pages/ecosystem/official_APIpage.md +++ b/pages/ecosystem/official_APIpage.md @@ -1,72 +1,20 @@ --- -title: - en: OpenList APIPages - zh-CN: OpenList APIPages categories: - ecosystem - eco_official top: 970 --- -## What is OpenList APIPages { lang="en"} +# OpenList APIPages -## OpenList APIPages 是什么 { lang="zh-CN" } +## OpenList APIPages 是什么 ### [OpenListTeam/OpenList-APIPages](https://github.com/OpenListTeam/OpenList-APIPages) -:::en -[OpenListTeam/OpenList-APIPages](https://github.com/OpenListTeam/OpenList-APIPages) is a utility website led by [@PIKACHUIM](https://github.com/PIKACHUIM) and collaboratively developed with other [main contributors](https://github.com/OpenListTeam/OpenList-APIPages/graphs/contributors). The frontend is primarily used for initial authorization and obtaining refresh tokens of cloud storage clients, while the backend mainly supports a feature called "Online API", which enables remote token refresh functionality while protecting developer client secrets. -::: -:::zh-CN [OpenListTeam/OpenList-APIPages](https://github.com/OpenListTeam/OpenList-APIPages) 是一个由 [@PIKACHUIM](https://github.com/PIKACHUIM) 牵头,和其他[主要贡献者](https://github.com/OpenListTeam/OpenList-APIPages/graphs/contributors)共同协作完成的工具型网站,前端主要用于实现对网盘客户端的初始授权和刷新令牌,而后端则主要支撑了一个名为“在线API”的功能,可以在保护开发者客户端密钥的前提下,实现对用户令牌的远程刷新功能。 -::: - -## Why OpenList APIPages is needed { lang="en"} - -## 为什么需要 OpenList APIPages { lang="zh-CN" } - -:::en -Cloud Storage Background: - -- Most domestic cloud storage services do not provide official API access to individuals, or the application process is cumbersome, which is not conducive to rapid deployment. -- According to the cloud storage providers' management requirements, the obtained client keys must not be leaked. -- User authorization is in the form of refresh tokens, which need to be periodically combined with client keys to obtain refreshed access tokens from the cloud storage servers. -- API calls require the user's latest access tokens. - -Solution: - -- Community volunteers provide qualification certification and apply for API access permissions from cloud storage officials. -- Use relay servers to protect client keys. -- Periodically send refresh tokens to designated relay servers, which use built-in client keys to refresh access tokens. -- The relay server sends access tokens back to the OpenList client. - -The relay server described above is the backend functionality of the Online API server/APIPages, which works as follows: - -```mermaid -sequenceDiagram - participant OpenList - participant OnlineAPI as Online API Server - participant Server as Cloud Storage Server - - OpenList ->> OnlineAPI: Refresh Token - OnlineAPI ->> Server: Cloud Storage Client Key - Server -->> OnlineAPI: Access Token - OnlineAPI -->> OpenList: Access Token - OpenList ->> Server: Access Token - OpenList <<->> Server: Cloud Storage Data -``` - -The frontend part of APIPages also solves the following problems: - -- Initial authorization verification for different cloud storage services. -- Authorization while protecting built-in client keys. -- Authorization with custom client keys. -- Providing callback addresses for custom client keys. -- Other practical functions for cloud storage mounting. +## 为什么需要 OpenList APIPages -::: -:::zh-CN 网盘背景: - 大部分国内网盘没有对个人开放官方API调用的权限,或者申请过程繁琐,不利于快速部署。 @@ -104,44 +52,19 @@ APIPages的前端部分同时解决了如下问题: - 提供自定义客户端密钥下的的回调地址。 - 其他网盘挂载的实用功能。 -::: - -## How to use OpenList APIPages { lang="en"} - -## 如何使用 OpenList APIPages { lang="zh-CN" } +## 如何使用 OpenList APIPages -## When OpenList APIPages is not needed { lang="en"} - -## 什么时候不需要 OpenList APIPages { lang="zh-CN" } - -:::en -:::tip -For ordinary users, we strongly recommend using API servers provided by the community to reduce deployment difficulty. - -If you encounter failures in the network section, we recommend that you resolve. +## 什么时候不需要 OpenList APIPages -::: -:::zh-CN :::tip 对于普通用户,我们强烈建议使用由社区提供的API服务器以降低部署难度。 如果在网络部分遇到故障,我们建议您先行解决网络问题。 ::: -:::en -If you choose not to use community-provided servers or deploy your own server, please verify the following content: -- 1. Have completely read the documentation related to the corresponding cloud storage driver. -- 2. Have completely read the open API development documentation provided by the corresponding cloud storage service. -- 3. Can read and understand the project code, have certain debugging capabilities, and can understand the corresponding logs and error messages. -- 4. Fully understand the rights granted to you by AGPLv3 and the parts we should be responsible for. -- 5. Can use basic GitHub functions and know how to **correctly** submit issues/pull requests to us. -- 6. Please remember that we have no way to solve your network problems. - -::: -:::zh-CN 如果您选择不使用社区提供的服务器,或者自己部署服务器,请对照一下内容进行核实 - 1.完整阅读了对应网盘驱动相关内容的文档。 @@ -151,44 +74,9 @@ If you choose not to use community-provided servers or deploy your own server, p - 5.可以使用GitHub的基本功能,知道如何**正确的**向我们提交Issue/Pull Request。 - 6.请记住,我们没有任何办法可以解决您遇到的网络问题。 -::: - -## APIPages Deployment Tutorial { lang="en"} - -## APIPages 的部署教程 { lang="zh-CN" } - -### One-Click Deployment { lang="en"} - -### 一键部署 { lang="zh-CN" } - -:::en - -- EdgeOne Functions International - - - Deploy with EdgeOne Pages - - -After deployment, please log in to the [EdgeOne Functions console](https://console.tencentcloud.com/edgeone/pages) to modify environment variables. Please refer to the [Variable Description](#variable-description) section. - -- EdgeOne Functions China - - - Deploy with EdgeOne Pages - - -After deployment, please log in to the [EdgeOne Functions console](https://console.cloud.tencent.com/edgeone/pages) to modify environment variables. Please refer to the [Variable Description](#variable-description) section. - -- Cloudflare Workers Global +## APIPages 的部署教程 - - Deploy to Cloudflare Workers - - -After deployment, please log in to the [Cloudflare Workers console](https://dash.cloudflare.com/) to modify environment variables. Please refer to the [Variable Description](#variable-description) section. - -::: -:::zh-CN +### 一键部署 - EdgeOne Functions 国际站 @@ -214,87 +102,7 @@ After deployment, please log in to the [Cloudflare Workers console](https://dash 部署完成后,请登录[Cloudflare Worker后台](https://dash.cloudflare.com/),修改环境变量,请参考[变量说明](#变量说明)部分 -::: - -### Container Deployment { lang="en" } - -### 容器部署 { lang="zh-CN" } - -:::en - -- Pull image - -``` -docker pull openlistteam/openlist_api_server -``` - -or - -``` -docker pull ghcr.io/openlistteam/openlist_api_server:latest -``` - -- Start project - -``` -docker run -d --name oplist-api-server \ - -p 3000:3000 \ - -e OPLIST_MAIN_URLS="api.example.com" \ - -e OPLIST_PROXY_API="gts.example.com" \ - -e OPLIST_ONEDRIVE_UID= `#optional` \ - -e OPLIST_ONEDRIVE_KEY= `#optional` \ - -e OPLIST_ALICLOUD_UID= `#optional` \ - -e OPLIST_ALICLOUD_KEY= `#optional` \ - -e OPLIST_BAIDUYUN_UID= `#optional` \ - -e OPLIST_BAIDUYUN_KEY= `#optional` \ - -e OPLIST_BAIDUYUN_EXT= `#optional` \ - -e OPLIST_CLOUD115_UID= `#optional` \ - -e OPLIST_CLOUD115_KEY= `#optional` \ - -e OPLIST_GOOGLEUI_UID= `#optional` \ - -e OPLIST_GOOGLEUI_KEY= `#optional` \ - -e OPLIST_YANDEXUI_UID= `#optional` \ - -e OPLIST_YANDEXUI_KEY= `#optional` \ - -e OPLIST_DROPBOXS_UID= `#optional` \ - -e OPLIST_DROPBOXS_KEY= `#optional` \ - -e OPLIST_QUARKPAN_UID= `#optional` \ - -e OPLIST_QUARKPAN_KEY= `#optional` \ - openlistteam/openlist_api_server:latest -``` - -- You can replace the image with ghcr: - - ``` - ghcr.io/openlistteam/openlist_api_server:latest - ``` - -- **Please make sure to modify your environment variables according to the environment variables below** - -- Environment Variable Description - -| Variable Name | Required | Variable Type | Variable Description | -| --------------------- | -------- | ------------- | ------------------------------------------------------------- | -| `OPLIST_MAIN_URLS` | Yes | string | Bind main domain, example: api.example.com | -| `OPLIST_PROXY_API` | No | string | Nodes deployed in mainland China need to specify Google proxy | -| `OPLIST_ONEDRIVE_UID` | No | string | OneDrive Client ID | -| `OPLIST_ONEDRIVE_KEY` | No | string | OneDrive Client Secret | -| `OPLIST_ALICLOUD_UID` | No | string | AliCloud Drive Developer AppID | -| `OPLIST_ALICLOUD_KEY` | No | string | AliCloud Drive Developer AppKey | -| `OPLIST_BAIDUYUN_UID` | No | string | Baidu NetDisk Application UID | -| `OPLIST_BAIDUYUN_KEY` | No | string | Baidu NetDisk Application Secret AppKey | -| `OPLIST_BAIDUYUN_EXT` | No | string | Baidu NetDisk Application SecretKey | -| `OPLIST_CLOUD115_UID` | No | string | 115 NetDisk Application ID | -| `OPLIST_CLOUD115_KEY` | No | string | 115 NetDisk Application Secret | -| `OPLIST_GOOGLEUI_UID` | No | string | Google Client ID | -| `OPLIST_GOOGLEUI_KEY` | No | string | Google Global API Key | -| `OPLIST_YANDEXUI_UID` | No | string | Yandex Application ID | -| `OPLIST_YANDEXUI_KEY` | No | string | Yandex Application Secret | -| `OPLIST_DROPBOXS_UID` | No | string | Dropbox Application ID | -| `OPLIST_DROPBOXS_KEY` | No | string | Dropbox Application Secret | -| `OPLIST_QUARKPAN_UID` | No | string | QuarkPan Application ID | -| `OPLIST_QUARKPAN_KEY` | No | string | QuarkPan Application Secret | - -::: -:::zh-CN +### 容器部署 - 拉取镜像 @@ -365,88 +173,7 @@ docker run -d --name oplist-api-server \ | `OPLIST_QUARKPAN_UID` | 否 | string | 夸克云盘x应用ID | | `OPLIST_QUARKPAN_KEY` | 否 | string | 夸克云盘应用密钥 | -::: - -### Edge Deployment { lang="en" } - -### 边缘部署 { lang="zh-CN" } - -:::en - -- Clone code - -```shell -git clone https://github.com/OpenListTeam/OpenList-APIPages.git -``` - -- Modify configuration (CloudFlare only) - -Create and modify `wrangler.jsonc` - -```shell -cp wrangler.example.jsonc wrangler.encrypt.jsonc -``` - -Modify variable information: - -- MAIN_URLS: Domain name for deployment callback address -- Other parameters: Application information for each cloud storage service - -``` - "vars": { - "MAIN_URLS": "api.example.com", - "PROXY_API": "gts.example.com", - "onedrive_uid": "*****************************", - "onedrive_key": "*****************************", - "alicloud_uid": "*****************************", - "alicloud_key": "*****************************", - "baiduyun_uid": "*****************************", - "baiduyun_key": "*****************************", - "baiduyun_ext": "*****************************", - "cloud115_uid": "*****************************", - "cloud115_key": "*****************************", - "googleui_uid": "*****************************", - "googleui_key": "*****************************", - "yandexui_uid": "*****************************", - "yandexui_key": "*****************************", - "dropboxs_uid": "*****************************", - "dropboxs_key": "*****************************", - "quarkpan_uid": "*****************************", - "quarkpan_key": "*****************************" - }, -``` - -- Test code - -```shell -npm install - -# Run in Cloudflare Worker environment -npm run dev-cf - -# Run in Edgeone Functions environment -npm run dev-eo - -# Run in Node Service Work environment -npm run dev-js - -``` - -- Deploy project - -```shell -# Deploy in Cloudflare Worker environment -npm run deploy-cf - -# Deploy in Edgeone Functions environment -npm run deploy-eo - -# Run locally in Node Service Work -npm build-js && npm deploy-js -``` - -::: -:::zh-CN +### 边缘部署 - 克隆代码 @@ -520,70 +247,32 @@ npm run deploy-eo npm build-js && npm deploy-js ``` -::: - -### Variable Description { lang="en" } - -### 变量说明 { lang="zh-CN" } - -:::en -| Variable Name | Required | Variable Type | Variable Description | -| -------------- | ---- | -------- |-------------------| -| `MAIN_URLS` | Yes | string | Bind main domain, example: api.example.com | -| `PROXY_API` | No | string | Nodes deployed in mainland China need to specify Google proxy | -| `onedrive_uid` | No | string | OneDrive Client ID | -| `onedrive_key` | No | string | OneDrive Client Secret | -| `alicloud_uid` | No | string | AliCloud Drive Developer AppID | -| `alicloud_key` | No | string | AliCloud Drive Developer AppKey | -| `baiduyun_uid` | No | string | Baidu NetDisk Application ID | -| `baiduyun_key` | No | string | Baidu NetDisk Application Secret AppKey | -| `baiduyun_ext` | No | string | Baidu NetDisk Application SecretKey | -| `cloud115_uid` | No | string | 115 NetDisk Application ID | -| `cloud115_key` | No | string | 115 NetDisk Application Secret | -| `googleui_uid` | No | string | Google Client ID | -| `googleui_key` | No | string | Google Global API Key | -| `yandexui_uid` | No | string | Yandex Application ID | -| `yandexui_key` | No | string | Yandex Application Secret | -| `dropboxs_uid` | No | string | Dropbox Application ID | -| `dropboxs_key` | No | string | Dropbox Application Secret | -| `quarkpan_uid` | No | string | QuarkPan Application ID | -| `quarkpan_key` | No | string | QuarkPan Application Secret | - -::: -:::zh-CN -| 变量名称 | 必要 | 变量类型 | 变量说明 | -| -------------- | ---- | -------- |-------------------| -| `MAIN_URLS` | 是 | string | 绑定主域名,示例:api.example.com | -| `PROXY_API` | 否 | string | 部署在大陆的节点需要指定代理谷歌 | -| `onedrive_uid` | 否 | string | OneDrive 客户端ID | -| `onedrive_key` | 否 | string | OneDrive 客户端密钥 | -| `alicloud_uid` | 否 | string | 阿里云盘开发者AppID | -| `alicloud_key` | 否 | string | 阿里云盘开发者AppKey | -| `baiduyun_uid` | 否 | string | 百度网盘应用ID | -| `baiduyun_key` | 否 | string | 百度网盘应用密钥AppKey | -| `baiduyun_ext` | 否 | string | 百度网盘应用密钥SecretKey | -| `cloud115_uid` | 否 | string | 115网盘应用ID | -| `cloud115_key` | 否 | string | 115网盘应用密钥 | -| `googleui_uid` | 否 | string | 谷歌客户端ID | -| `googleui_key` | 否 | string | 谷歌全局API Key | -| `yandexui_uid` | 否 | string | Yandex应用ID | -| `yandexui_key` | 否 | string | Yandex应用密钥 | -| `dropboxs_uid` | 否 | string | Dropboxx应用ID | -| `dropboxs_key` | 否 | string | Dropbox应用密钥 | -| `quarkpan_uid` | 否 | string | 夸克云盘x应用ID | -| `quarkpan_key` | 否 | string | 夸克云盘应用密钥 | - -::: +### 变量说明 + +| 变量名称 | 必要 | 变量类型 | 变量说明 | +| -------------- | ---- | -------- | --------------------------------- | +| `MAIN_URLS` | 是 | string | 绑定主域名,示例:api.example.com | +| `PROXY_API` | 否 | string | 部署在大陆的节点需要指定代理谷歌 | +| `onedrive_uid` | 否 | string | OneDrive 客户端ID | +| `onedrive_key` | 否 | string | OneDrive 客户端密钥 | +| `alicloud_uid` | 否 | string | 阿里云盘开发者AppID | +| `alicloud_key` | 否 | string | 阿里云盘开发者AppKey | +| `baiduyun_uid` | 否 | string | 百度网盘应用ID | +| `baiduyun_key` | 否 | string | 百度网盘应用密钥AppKey | +| `baiduyun_ext` | 否 | string | 百度网盘应用密钥SecretKey | +| `cloud115_uid` | 否 | string | 115网盘应用ID | +| `cloud115_key` | 否 | string | 115网盘应用密钥 | +| `googleui_uid` | 否 | string | 谷歌客户端ID | +| `googleui_key` | 否 | string | 谷歌全局API Key | +| `yandexui_uid` | 否 | string | Yandex应用ID | +| `yandexui_key` | 否 | string | Yandex应用密钥 | +| `dropboxs_uid` | 否 | string | Dropboxx应用ID | +| `dropboxs_key` | 否 | string | Dropbox应用密钥 | +| `quarkpan_uid` | 否 | string | 夸克云盘x应用ID | +| `quarkpan_key` | 否 | string | 夸克云盘应用密钥 | + +## 社区 APIPages -## Community APIPages { lang="en"} - -## 社区 APIPages { lang="zh-CN" } - -:::en -:::tip -The following servers are built and provided by community volunteers. Once used, user authorization credentials will inevitably be sent to the servers over the network. This project is licensed under AGPLv3 and only guarantees to provide the source code "as is". Users should verify the deployed content at their own discretion. -::: -:::zh-CN :::tip 以下服务器由社区志愿者搭建并提供服务,一旦使用将不可避免的将用户授权凭证通过网络形式发送至服务器,本项目根据AGPLv3许可,只保证在源代码层面按“原样”提供,对于部署后的内容,请用户自行甄别。 ::: @@ -593,11 +282,4 @@ The following servers are built and provided by community volunteers. Once used, ...... -:::en -[Welcome to submit more community servers](https://github.com/OpenListTeam/OpenList-Docs/pulls) - -::: -:::zh-CN [欢迎提交更多社区服务器](https://github.com/OpenListTeam/OpenList-Docs/pulls) - -::: diff --git a/pages/ecosystem/official_desktop.md b/pages/ecosystem/official_desktop.md index 33db949c2..ea938a4f3 100644 --- a/pages/ecosystem/official_desktop.md +++ b/pages/ecosystem/official_desktop.md @@ -1,48 +1,21 @@ --- -title: - en: OpenList Desktop - zh-CN: OpenList Desktop categories: - ecosystem - eco_official top: 980 --- -## What is OpenList Desktop { lang="en" } +# OpenList Desktop -## OpenList Desktop 是什么 { lang="zh-CN" } +## OpenList Desktop 是什么 ### [OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) -:::en -[OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) is a powerful cross-platform desktop application led by [@Kuingsmile](https://github.com/Kuingsmile) and collaboratively developed with other [main contributors](https://github.com/OpenListTeam/OpenList-Desktop/graphs/contributors). Built with Vue 3, Tauri, and Rust, it provides a user-friendly interface for managing OpenList services and performing local mounts via Rclone. The application serves as a comprehensive solution for managing OpenList file management services, mounting and managing cloud storage (WebDAV), monitoring service status and uptime, and providing system tray integration for background operations. - -::: - -:::zh-CN [OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) 是一个功能强大的跨平台桌面应用程序,由 [@Kuingsmile](https://github.com/Kuingsmile) 牵头,和其他[主要贡献者](https://github.com/OpenListTeam/OpenList-Desktop/graphs/contributors)共同协作完成。基于 Vue 3、Tauri 和 Rust 构建,为管理 OpenList 服务和通过 Rclone 进行本地挂载提供用户友好的界面。该应用程序是一个全面的解决方案,用于管理 OpenList 文件管理服务、挂载和管理云存储(WebDAV)、监控服务运行状态和提供系统托盘集成以进行后台操作。 -::: - -## Features { lang="en" } - -## 功能特性 { lang="zh-CN" } - -### Core Features { lang="en" } +## 功能特性 -### 核心功能 { lang="zh-CN" } - -:::en - -- **OpenList Service Management**: Start, stop, and monitor OpenList core services -- **Local Mounting**: Mount via Rclone to the local file system -- **Real-time Monitoring**: Track service status, uptime, and performance metrics -- **Process Management**: Advanced process control with auto-restart capabilities -- **System Tray**: Background operation with system tray notifications - -::: - -:::zh-CN +### 核心功能 - **OpenList 服务管理**:启动、停止和监控 OpenList 核心服务 - **本地挂载**:通过 Rclone 挂载至本地文件系统 @@ -50,23 +23,7 @@ top: 980 - **进程管理**:具有自动重启功能的高级进程控制 - **系统托盘**:带系统托盘通知的后台操作 -::: - -### Management Features { lang="en" } - -### 管理功能 { lang="zh-CN" } - -:::en - -- **Service Control**: Start/stop/restart OpenList and Rclone services -- **Configuration Management**: GUI-based configuration for all services -- **Log Monitoring**: Real-time log viewing and management -- **Update Management**: Automatic update checking and installation -- **Auto-startup**: Configure applications to start with system boot - -::: - -:::zh-CN +### 管理功能 - **服务控制**:启动/停止/重启 OpenList 和 Rclone 服务 - **配置管理**:所有服务的基于 GUI 的配置 @@ -74,79 +31,23 @@ top: 980 - **更新管理**:自动更新检查和安装 - **自动启动**:配置应用程序与系统一起启动 -::: - -## Installation { lang="en" } - -## 安装 { lang="zh-CN" } - -### System Requirements { lang="en" } - -### 系统要求 { lang="zh-CN" } - -:::en +## 安装 -- **Operating System**: Windows 10+, macOS 10.15+, or Linux (Ubuntu 18.04+) - -::: - -:::zh-CN +### 系统要求 - **操作系统**:Windows 10+、macOS 10.15+ 或 Linux(Ubuntu 18.04+) -::: - -### Download Options { lang="en" } - -### 下载选项 { lang="zh-CN" } - -#### GitHub Releases (Recommended) { lang="en" } +### 下载选项 -#### GitHub 发行版(推荐) { lang="zh-CN" } +#### GitHub 发行版(推荐) -:::en -Download the latest release from [GitHub Releases](https://github.com/OpenListTeam/OpenList-Desktop/releases): - -- **Windows**: `OpenList-Desktop_x.x.x_{arch}-setup.exe` -- **macOS**: `OpenList-Desktop_x.x.x_{arch}.dmg` -- **Linux**: `OpenList-Desktop_x.x.x_{arch}.deb` or `OpenList-Desktop_x.x.x_{arch}.rpm` - -::: - -:::zh-CN 从 [GitHub Releases](https://github.com/OpenListTeam/OpenList-Desktop/releases) 下载最新版本: - **Windows**:`OpenList-Desktop_x.x.x_{arch}-setup.exe` - **macOS**:`OpenList-Desktop_x.x.x_{arch}.dmg` - **Linux**:`OpenList-Desktop_x.x.x_{arch}.deb` 或 `OpenList-Desktop_x.x.x_{arch}.rpm` -::: - -#### Build from Source { lang="en" } - -#### 从源码构建 { lang="zh-CN" } - -:::en - -```bash -# Clone the repository -git clone https://github.com/OpenListTeam/OpenList-Desktop.git -cd openlist-desktop - -# Install dependencies -yarn install - -# Prepare development environment -yarn run prebuild:dev - -# Build the application -yarn run build -yarn run tauri build -``` - -::: - -:::zh-CN +#### 从源码构建 ```bash # 克隆仓库 @@ -164,98 +65,31 @@ yarn run build yarn run tauri build ``` -::: - -### Installation Steps { lang="en" } - -### 安装步骤 { lang="zh-CN" } - -#### Windows { lang="en" } - -#### Windows { lang="zh-CN" } +### 安装步骤 -##### Using Installer { lang="en" } +#### Windows -##### 使用安装程序 { lang="zh-CN" } - -:::en - -1. Download the `.exe` installer -2. Run the installer as Administrator -3. Follow the installation wizard -4. Launch from Start Menu or Desktop shortcut - -::: - -:::zh-CN +##### 使用安装程序 1. 下载 `.exe` 安装程序 2. 以管理员身份运行安装程序 3. 按照安装向导进行操作 4. 从开始菜单或桌面快捷方式启动 -::: - -##### Using Winget { lang="en" } - -##### 使用 Winget { lang="zh-CN" } - -:::en +##### 使用 Winget ```bash winget install OpenListTeam.OpenListDesktop ``` -::: - -:::zh-CN - -```bash -winget install OpenListTeam.OpenListDesktop -``` - -::: - -#### macOS { lang="en" } - -#### macOS { lang="zh-CN" } - -:::en - -1. Download the `.dmg` file -2. Open the DMG and drag OpenList Desktop to Applications -3. Right-click and select "Open" (first time only) -4. Grant necessary permissions when prompted - -::: - -:::zh-CN +#### macOS 1. 下载 `.dmg` 文件 2. 打开 DMG 并将 OpenList Desktop 拖到应用程序文件夹 3. 右键单击并选择"打开"(仅首次) 4. 在提示时授予必要权限 -::: - -#### Linux { lang="en" } - -#### Linux { lang="zh-CN" } - -:::en - -1. Download the `.deb` or `.rpm` package -2. Use your package manager to install: - - ```bash - sudo dpkg -i OpenList-Desktop_x.x.x_amd64.deb - # or - sudo rpm -i OpenList-Desktop_x.x.x_amd64.rpm - ``` - -::: - -:::zh-CN +#### Linux 1. 下载 `.deb` 或 `.rpm` 包 2. 使用包管理器安装: @@ -266,27 +100,9 @@ winget install OpenListTeam.OpenListDesktop sudo rpm -i OpenList-Desktop_x.x.x_amd64.rpm ``` -::: - -## Usage { lang="en" } - -## 使用说明 { lang="zh-CN" } - -### First Launch { lang="en" } - -### 首次启动 { lang="zh-CN" } +## 使用说明 -:::en - -It is recommended to run OpenList Desktop with Administrator privileges on first launch to ensure proper service installation and configuration. - -1. **Initial Setup**: On first launch, the application will guide you through initial configuration -2. **Service Installation**: Install the OpenList service when prompted -3. **Storage Configuration**: Configure your first cloud storage connection - -::: - -:::zh-CN +### 首次启动 建议在首次启动时通过管理员权限运行 OpenList Desktop,以确保正确安装和配置服务。 @@ -294,53 +110,16 @@ It is recommended to run OpenList Desktop with Administrator privileges on first 2. **服务安装**:在提示时安装 OpenList 服务 3. **存储配置**:配置您的第一个云存储连接 -::: - -### Basic Operations { lang="en" } +### 基本操作 -### 基本操作 { lang="zh-CN" } - -#### Starting Services { lang="en" } - -#### 启动服务 { lang="zh-CN" } - -:::en - -```bash -Dashboard → Quick Actions → Start OpenList Core -Dashboard → Quick Actions → Start Rclone Backend -``` - -::: - -:::zh-CN +#### 启动服务 ```bash 仪表板 → 快速操作 → 启动 OpenList 核心 仪表板 → 快速操作 → 启动 Rclone 后端 ``` -::: - -#### Adding Cloud Storage { lang="en" } - -#### 添加云存储 { lang="zh-CN" } - -:::en - -1. Navigate to **Mount** tab -2. Click **Add Remote** button -3. Configure storage settings: - - **Name**: Unique identifier for your storage - - **Type**: Storage provider (WebDAV) - - **URL**: Storage endpoint URL - - **Credentials**: Username and password - - **Mount Point**: Local directory path -4. Click **Save** and **Mount** - -::: - -:::zh-CN +#### 添加云存储 1. 导航到 **挂载** 选项卡 2. 点击 **添加远程** 按钮 @@ -352,70 +131,15 @@ Dashboard → Quick Actions → Start Rclone Backend - **挂载点**:本地目录路径 4. 点击 **保存** 和 **挂载** -::: - -#### Monitoring Operations { lang="en" } - -#### 监控操作 { lang="zh-CN" } - -:::en - -- **Service Status**: Check the dashboard for service health indicators -- **Logs**: Use the Logs tab to monitor system operations -- **Performance**: View uptime and response metrics on the dashboard - -::: - -:::zh-CN +#### 监控操作 - **服务状态**:检查仪表板上的服务健康指示器 - **日志**:使用日志选项卡监控系统操作 - **性能**:在仪表板上查看运行时间和响应指标 -::: +## 本地开发 -## Local Development { lang="en" } - -## 本地开发 { lang="zh-CN" } - -### Prerequisites { lang="en" } - -### 环境要求 { lang="zh-CN" } - -:::en - -- **Node.js**: v22+ with yarn -- **Rust**: Latest nightly version -- **Git**: Version control - -1. **Clone the repository** - - ```bash - git clone https://github.com/OpenListTeam/OpenList-Desktop.git - cd openlist-desktop - ``` - -2. **Install dependencies** - - ```bash - yarn install - ``` - -3. **Prepare development environment** - - ```bash - yarn run prebuild:dev - ``` - -4. **Start development server** - - ```bash - yarn tauri dev - ``` - -::: - -:::zh-CN +### 环境要求 - **Node.js**:v22+ 和 yarn - **Rust**:最新 nightly 版本 @@ -446,23 +170,7 @@ Dashboard → Quick Actions → Start Rclone Backend yarn tauri dev ``` -::: - -### Building { lang="en" } - -### 构建 { lang="zh-CN" } - -:::en - -```bash -# Build the application -yarn run build -yarn run tauri build -``` - -::: - -:::zh-CN +### 构建 ```bash # 构建应用程序 @@ -470,31 +178,10 @@ yarn run build yarn run tauri build ``` -::: +## 许可证与法律 -## License & Legal { lang="en" } +### 许可证 -## 许可证与法律 { lang="zh-CN" } - -### License { lang="en" } - -### 许可证 { lang="zh-CN" } - -:::en -This desktop application project is licensed under the **[GNU General Public License v3.0 (GPL-3.0)](https://www.gnu.org/licenses/gpl-3.0.en.html)**. - -- **Freedom to Use**: You can use, modify, and distribute this application -- **Copyleft**: Any derivative works must also be licensed under GPL-3.0 -- **Source Code**: You must provide source code when distributing the application -- **Attribution**: You must preserve copyright notices and license information - -For the full license text, see the [LICENSE](https://github.com/OpenListTeam/OpenList-Desktop/blob/main/LICENSE) file. - -By contributing to this project, you agree that your contributions will be licensed under the same GPL-3.0 license. - -::: - -:::zh-CN 本桌面应用程序项目采用 **[GNU General Public License v3.0 (GPL-3.0)](https://www.gnu.org/licenses/gpl-3.0.en.html)** 许可证。 - **使用自由**:您可以使用、修改和分发此应用程序 @@ -505,5 +192,3 @@ By contributing to this project, you agree that your contributions will be licen 完整的许可证文本请参见 [LICENSE](https://github.com/OpenListTeam/OpenList-Desktop/blob/main/LICENSE) 文件。 通过为本项目做出贡献,您同意您的贡献将采用相同的 GPL-3.0 许可证。 - -::: diff --git a/pages/ecosystem/official_docs.md b/pages/ecosystem/official_docs.md index a8cf7d714..6ce5741a6 100644 --- a/pages/ecosystem/official_docs.md +++ b/pages/ecosystem/official_docs.md @@ -1,101 +1,28 @@ --- -title: - en: OpenList Docs - zh-CN: OpenList Docs categories: - ecosystem - eco_official top: 990 --- -## What is OpenList Docs { lang="en" } +# OpenList Docs -## OpenList Docs 是什么 { lang="zh-CN" } +## OpenList Docs 是什么 ### [OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) -:::en -[OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) is the official documentation website for OpenList, led by [@cxw620](https://github.com/cxw620) and collaboratively developed with other [main contributors](https://github.com/OpenListTeam/OpenList-Docs/graphs/contributors). Built with Valaxy framework, it provides comprehensive documentation for OpenList, including installation guides, configuration instructions, API references, and ecosystem information. The documentation supports multiple languages and features real-time builds from the GitHub repository. +[OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) 是 OpenList 的官方文档网站,由 [@cxw620](https://github.com/cxw620) 牵头,和其他[主要贡献者](https://github.com/OpenListTeam/OpenList-Docs/graphs/contributors)共同协作完成。基于 VitePress 构建,为 OpenList 提供全面的文档,包括安装指南、配置说明、API 参考和生态系统信息。文档支持多种语言,并基于 GitHub 仓库实时构建。 -::: +## 如何使用 OpenList Docs -:::zh-CN -[OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) 是 OpenList 的官方文档网站,由 [@cxw620](https://github.com/cxw620) 牵头,和其他[主要贡献者](https://github.com/OpenListTeam/OpenList-Docs/graphs/contributors)共同协作完成。基于 Valaxy 框架构建,为 OpenList 提供全面的文档,包括安装指南、配置说明、API 参考和生态系统信息。文档支持多种语言,并基于 GitHub 仓库实时构建。 - -::: - -## How to use OpenList Docs { lang="en" } - -## 如何使用 OpenList Docs { lang="zh-CN" } - -:::en -You can directly access [doc.oplist.org.cn](https://doc.oplist.org.cn/) to view the documentation. - -::: - -:::zh-CN 可直接在线访问[doc.oplist.org.cn](https://doc.oplist.org.cn/) 查阅文档。 -::: - -## Local Development { lang="en" } - -### Prerequisites { lang="en" } - -:::en - -- **Node.js**: Required for running the development environment -- **pnpm**: Recommended package manager -- **Git**: For version control and cloning the repository - -1. **Clone the repository** - - ```bash - git clone https://github.com/OpenListTeam/OpenList-Docs.git - cd OpenList-Docs - ``` - -2. **Install dependencies** - - ```bash - pnpm install - ``` - -3. **Start development server** +## 本地开发 - ```bash - pnpm dev - ``` - -4. **Open in browser** - - The documentation site will be available at `http://localhost:4859` - -::: - -### Building { lang="en" } - -:::en -In Windows, you may need to run `npm install -g win-node-env` to solve the `NODE_OPTIONS` environment variable problem. - -```bash -# Build static site -pnpm build - -# Preview build -pnpm serve -``` +### 环境要求 -::: - -## 本地开发 { lang="zh-CN" } - -### 环境要求 { lang="zh-CN" } - -:::zh-CN - -- **Node.js**:运行开发环境所需 -- **pnpm**:推荐的包管理器 +- **Node.js** 24 或更高版本(见 `.nvmrc`) +- **Bun**:包管理器和脚本运行器 - **Git**:用于版本控制和克隆仓库 1. **克隆仓库** @@ -108,167 +35,71 @@ pnpm serve 2. **安装依赖** ```bash - pnpm install + bun install ``` 3. **启动开发服务器** ```bash - pnpm dev + bun run dev ``` 4. **在浏览器中打开** - 文档站点将在 `http://localhost:4859` 处可用 - -::: + 文档站点将在 `http://localhost:5173` 可用 -### 构建 { lang="zh-CN" } - -:::zh-CN -在Windows操作系统下,可能需要运行`npm install -g win-node-env`解决`NODE_OPTIONS`环境变量问题。 +### 构建 ```bash -# 构建静态站点 -pnpm build +# 构建静态站点到 dist/ +bun run build -# 预览构建 -pnpm serve +# 预览构建结果 +bun run preview ``` -::: - -### Development Tips { lang="en" } - -:::en -`:::tip` Markdown Parsing Issues -When writing documentation, please note these important parsing rules: - -1. **Language Block Syntax**: For language-specific content blocks, only use one closing `:::` at the end: - - ![Wrong Content Syntax - Light](/img/dev-tip/wrongcontent-light.png#light) - ![Wrong Content Syntax - Dark](/img/dev-tip/wrongcontent-dark.png#dark) +### 编写提示 - The above should be written as: +本站使用 [VitePress](https://vitepress.dev/zh/) 构建,其 [Markdown 扩展](https://vitepress.dev/zh/guide/markdown) 均可直接使用。 - ![Correct Content Syntax - Light](/img/dev-tip/correctcontent-light.png#light) - ![Correct Content Syntax - Dark](/img/dev-tip/correctcontent-dark.png#dark) +1. **每种语言一个文件**:中文页面位于 `pages/`,英文页面位于 `pages/en/` 下相同的相对路径,例如 `pages/guide/drivers/s3.md` 与 `pages/en/guide/drivers/s3.md`。修改页面时请同时更新两个文件。 - The language will automatically switch when encountering `:::en`. +2. **页面标题**:正文第一行即为标题,使用一级标题(`# 标题`),不要在 frontmatter 中添加 `title`。 -2. **Title Language Specification**: Titles must use `{ lang="en" }` syntax and cannot use language blocks: +3. **侧边栏**:frontmatter 中设置了 `categories` 的页面会显示在侧边栏,并按 `top` 由高到低排序: - ![Correct Title Syntax - Light](/img/dev-tip/correcttitle-light.png#light) - ![Correct Title Syntax - Dark](/img/dev-tip/correcttitle-dark.png#dark) + ```md + --- + top: 895 + categories: + - guide + - drivers + --- - Do NOT use: - - ![Wrong Title Syntax - Light](/img/dev-tip/wrongtitle-light.png#light) - ![Wrong Title Syntax - Dark](/img/dev-tip/wrongtitle-dark.png#dark) - -3. **Text Format Convention**: For better readability in plain text environments, please follow the **English first, then Chinese** order when writing bilingual content. This makes it easier to observe and navigate the documentation structure in text editors and version control systems. - -4. **Line Break Before Closing `:::`**: Always add a line break before the closing `:::`, especially when using lists (`li`), otherwise formatting may be corrupted: - - ![Wrong Translate End Line Syntax - Light](/img/dev-tip/wrongtranslateendline-light.png#light) - ![Wrong Translate End Line Syntax - Dark](/img/dev-tip/wrongtranslateendline-dark.png#dark) - - The above should be written as: - - ![Correct Translate End Line Syntax - Light](/img/dev-tip/correcttranslateendline-light.png#light) - ![Correct Translate End Line Syntax - Dark](/img/dev-tip/correcttranslateendline-dark.png#dark) - -5. **Empty Line for Line Breaks**: Content needs an empty line to create line breaks. Without empty lines, content will render as continuous text: - - ![Wrong End Line Syntax - Light](/img/dev-tip/wrongendline-light.png#light) - ![Wrong End Line Syntax - Dark](/img/dev-tip/wrongendline-dark.png#dark) - - The above should be written as: - - ![Correct End Line Syntax - Light](/img/dev-tip/correctendline-light.png#light) - ![Correct End Line Syntax - Dark](/img/dev-tip/correctendline-dark.png#dark) - - Note: Lists (`li`) are not affected by this issue. - -::: - -### 开发提示 { lang="zh-CN" } - -:::zh-CN -`:::tip` Markdown 解析问题 -在编写文档时,请注意以下重要的解析规则: - -1. **语言块语法**:对于特定语言的内容块,结尾只需要一个 `:::`: - - ![错误内容语法 - Light](/img/dev-tip/wrongcontent-light.png#light) - ![错误内容语法 - Dark](/img/dev-tip/wrongcontent-dark.png#dark) - - 上面的写法应该改为: - - ![正确内容语法 - Light](/img/dev-tip/correctcontent-light.png#light) - ![正确内容语法 - Dark](/img/dev-tip/correctcontent-dark.png#dark) - - 当遇到 `:::en` 时语言会自动切换。 - -2. **标题语言指定**:标题必须使用 `{ lang="en" }` 语法,不能使用语言块: - - ![正确标题语法 - Light](/img/dev-tip/correcttitle-light.png#light) - ![正确标题语法 - Dark](/img/dev-tip/correcttitle-dark.png#dark) - - 不要使用: - - ![错误标题语法 - Light](/img/dev-tip/wrongtitle-light.png#light) - ![错误标题语法 - Dark](/img/dev-tip/wrongtitle-dark.png#dark) - -3. **文本格式约定**:为了方便在纯文本环境下的观察起见,在编写双语内容时请遵循**先英文后中文**的顺序。这样可以更容易在文本编辑器和版本控制系统中观察和导航文档结构。 - -4. **结束块 `:::` 前换行**:结束块的 `:::` 前最好换行,尤其是使用了列表(`li`),否则会被格式化乱掉: - - ![错误翻译结束行语法 - Light](/img/dev-tip/wrongtranslateendline-light.png#light) - ![错误翻译结束行语法 - Dark](/img/dev-tip/wrongtranslateendline-dark.png#dark) - - 上面的写法应该改为: - - ![正确翻译结束行语法 - Light](/img/dev-tip/correcttranslateendline-light.png#light) - ![正确翻译结束行语法 - Dark](/img/dev-tip/correcttranslateendline-dark.png#dark) - -5. **内容换行需要空行**:内容需要空一行才能换行。没有空行的话,内容会渲染成连续的文本: - - ![错误结束行语法 - Light](/img/dev-tip/wrongendline-light.png#light) - ![错误结束行语法 - Dark](/img/dev-tip/wrongendline-dark.png#dark) - - 上面的写法应该改为: - - ![正确结束行语法 - Light](/img/dev-tip/correctendline-light.png#light) - ![正确结束行语法 - Dark](/img/dev-tip/correctendline-dark.png#dark) - - 注意:列表(`li`)不涉及这个问题。 - -::: - -## License & Legal { lang="en" } - -### License { lang="en" } - -:::en -This documentation project is licensed under the **[GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html)**. + # 对象存储(S3) + ``` -- **Freedom to Use**: You can use, modify, and distribute this documentation -- **Copyleft**: Any derivative works must also be licensed under AGPL-3.0 -- **Network Use**: If you run a modified version on a server, you must provide the source code -- **Attribution**: You must preserve copyright notices and license information +4. **嵌套容器**:当 `::: tip` 等容器内还包含其他容器时,外层需要使用更多的冒号,否则第一个 `:::` 会直接关闭外层容器: -For the full license text, see the [LICENSE](https://github.com/OpenListTeam/OpenList-Docs/blob/main/LICENSE) file. + ```md + :::: details 示例 + ::: tip + 内层提示 + ::: + :::: + ``` -By contributing to this project, you agree that your contributions will be licensed under the same AGPL-3.0 license. +5. **浅色 / 深色图片**:在图片链接后添加 `#light` / `#dark`,图片只会在对应的颜色模式下显示: -::: + ```md + ![截图](/img/example-light.png#light) + ![截图](/img/example-dark.png#dark) + ``` -## 许可证与法律 { lang="zh-CN" } +## 许可证与法律 -### 许可证 { lang="zh-CN" } +### 许可证 -:::zh-CN 本文档项目采用 **[GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html)** 许可证。 - **使用自由**:您可以使用、修改和分发此文档 @@ -279,5 +110,3 @@ By contributing to this project, you agree that your contributions will be licen 完整的许可证文本请参见 [LICENSE](https://github.com/OpenListTeam/OpenList-Docs/blob/main/LICENSE) 文件。 通过为本项目做出贡献,您同意您的贡献将采用相同的 AGPL-3.0 许可证。 - -::: diff --git a/pages/ecosystem/official_mobile.md b/pages/ecosystem/official_mobile.md index 1af52ea50..b5447819d 100644 --- a/pages/ecosystem/official_mobile.md +++ b/pages/ecosystem/official_mobile.md @@ -1,44 +1,19 @@ --- -title: - en: OpenList Mobile - zh-CN: OpenList Mobile categories: - ecosystem - eco_official top: 975 --- -## What is OpenList Mobile { lang="en" } +# OpenList Mobile -## OpenList Mobile 是什么 { lang="zh-CN" } +## OpenList Mobile 是什么 ### [OpenListTeam/OpenList-Mobile](https://github.com/OpenListTeam/OpenList-Mobile) -:::en -[OpenListTeam/OpenList-Mobile](https://github.com/OpenListTeam/OpenList-Mobile) is a mobile file server application based on [OpenList](https://github.com/OpenListTeam/OpenList), built with Flutter framework. It turns your phone into a powerful file server with LAN file sharing, remote access, and online management capabilities. The application is collaboratively developed by the [main contributors](https://github.com/OpenListTeam/OpenList-Mobile/graphs/contributors) and features automatic updates that sync with the latest OpenList releases daily. - -::: - -:::zh-CN [OpenListTeam/OpenList-Mobile](https://github.com/OpenListTeam/OpenList-Mobile) 是一个基于 [OpenList](https://github.com/OpenListTeam/OpenList) 的移动端文件服务器应用,使用 Flutter 框架开发。它可以将您的手机变成一个强大的文件服务器,支持局域网文件共享、远程访问和在线管理功能。该应用由[主要贡献者](https://github.com/OpenListTeam/OpenList-Mobile/graphs/contributors)共同协作开发,具有自动更新功能,每日与最新的 OpenList 版本同步。 -::: - -## Features { lang="en" } - -## 功能特性 { lang="zh-CN" } - -:::en - -- **Mobile File Server**: Turn your mobile device into a file server -- **LAN File Sharing**: Share files within local network -- **Remote Access**: Access your files with internet connection -- **Online Management**: Web-based file management interface -- **Auto-Update**: Automatically syncs with latest OpenList releases - -::: - -:::zh-CN +## 功能特性 - **移动文件服务器**:将您的移动设备变成文件服务器 - **局域网文件共享**:在本地网络内共享文件 @@ -46,134 +21,42 @@ top: 975 - **在线管理**:基于 Web 的文件管理界面 - **自动更新**:自动与最新的 OpenList 版本同步 -::: - -## Installation { lang="en" } - -## 安装 { lang="zh-CN" } - -### System Requirements { lang="en" } - -### 系统要求 { lang="zh-CN" } - -:::en +## 安装 -- **Storage**: Minimum 100MB free space -- **Permissions**: Storage access permission required - -::: - -:::zh-CN +### 系统要求 - **存储空间**:至少 100MB 可用空间 - **权限**:需要存储访问权限 -::: - -### Download Options { lang="en" } - -### 下载选项 { lang="zh-CN" } - -#### GitHub Releases (Recommended) { lang="en" } - -#### GitHub 发行版(推荐) { lang="zh-CN" } +### 下载选项 -:::en -Download the latest release from [GitHub Releases](https://github.com/OpenListTeam/OpenList-Mobile/releases): +#### GitHub 发行版(推荐) -- **Stable Release**: [Latest Release](https://github.com/OpenListTeam/OpenList-Mobile/releases/latest) - Recommended for production use -- **Development Build**: [Development Build](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml) - Latest features and fixes - -::: - -:::zh-CN 从 [GitHub Releases](https://github.com/OpenListTeam/OpenList-Mobile/releases) 下载最新版本: - **稳定版本**:[发布版](https://github.com/OpenListTeam/OpenList-Mobile/releases/latest) - 推荐生产环境使用 - **开发版本**:[构建版](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml) - 最新功能和修复 -::: - -#### Auto-Update Feature { lang="en" } - -#### 自动更新功能 { lang="zh-CN" } - -:::en - -**Auto-Update**: [GitHub Actions](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml) automatically checks for the latest [OpenList](https://github.com/OpenListTeam/OpenList/releases) version twice daily (5 AM & 5 PM) and builds releases, ensuring you always have access to the latest version. - -::: - -:::zh-CN +#### 自动更新功能 **自动更新**:[GitHub Actions](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml) 每日早晚五点自动检查最新的 [OpenList](https://github.com/OpenListTeam/OpenList/releases) 版本并构建发布,确保始终使用最新版本。 -::: - -### Installation Steps { lang="en" } - -### 安装步骤 { lang="zh-CN" } - -#### Android { lang="en" } - -#### Android { lang="zh-CN" } +### 安装步骤 -:::en - -1. Download the `.apk` file from releases -2. Enable "Install from unknown sources" in Android settings -3. Install the APK file - -::: - -:::zh-CN +#### Android 1. 从发布页面下载 `.apk` 文件 2. 在 Android 设置中启用"允许安装未知来源应用" 3. 安装 APK 文件 -::: - -#### iOS { lang="en" } - -#### iOS { lang="zh-CN" } - -:::en - -iOS support is currently experimental and still in development. - -::: - -:::zh-CN +#### iOS iOS 支持目前处于实验阶段,仍在开发中。 -::: - -## License & Legal { lang="en" } - -## 许可证与法律 { lang="zh-CN" } - -### License { lang="en" } +## 许可证与法律 -### 许可证 { lang="zh-CN" } +### 许可证 -:::en -This mobile application project is licensed under the **[GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html)**. - -- **Freedom to Use**: You can use, modify, and distribute this application -- **Copyleft**: Any derivative works must also be licensed under AGPL-3.0 -- **Network Use**: If you run a modified version on a server, you must provide source code to users -- **Source Code**: You must provide source code when distributing the application -- **Attribution**: You must preserve copyright notices and license information - -For the full license text, see the [LICENSE](https://github.com/OpenListTeam/OpenList-Mobile/blob/main/LICENSE) file. - -By contributing to this project, you agree that your contributions will be licensed under the same AGPL-3.0 license. - -::: - -:::zh-CN 本移动应用程序项目采用 **[GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html)** 许可证。 - **使用自由**:您可以使用、修改和分发此应用程序 @@ -185,5 +68,3 @@ By contributing to this project, you agree that your contributions will be licen 完整的许可证文本请参见 [LICENSE](https://github.com/OpenListTeam/OpenList-Mobile/blob/main/LICENSE) 文件。 通过为本项目做出贡献,您同意您的贡献将采用相同的 AGPL-3.0 许可证。 - -::: diff --git a/pages/ecosystem/official_proxy.md b/pages/ecosystem/official_proxy.md index 509795787..c5098f46c 100644 --- a/pages/ecosystem/official_proxy.md +++ b/pages/ecosystem/official_proxy.md @@ -1,38 +1,18 @@ --- -title: - en: OpenList Proxy - zh-CN: OpenList Proxy categories: - ecosystem - eco_official top: 960 --- -## What is OpenList Proxy { lang="en" } +# OpenList Proxy -## OpenList Proxy 是什么 { lang="zh-CN" } +## OpenList Proxy 是什么 -:::en -[**OpenList Proxy**](https://github.com/OpenListTeam/OpenList-Proxy) is a simple implementation for proxying OpenList's **download traffic**. With this tool, you can isolate the server traffic of the OpenList deployment from the traffic required for downloads, thereby reducing the traffic consumption of the main server or speeding up downloads. - -::: -:::zh-CN [**OpenList Proxy**](https://github.com/OpenListTeam/OpenList-Proxy),是一个用于代理OpenList的**下载流量**的简易实现,通过该工具,可以将部署OpenList的服务器流量和下载所需要的流量隔离开,从而降低对主服务器的流量消耗或加快下载速度。 -::: - -## How to use OpenList Proxy { lang="en" } +## 如何使用 OpenList Proxy -## 如何使用 OpenList Proxy { lang="zh-CN" } - -:::en -:::danger -Cloudflare has explicitly prohibited the use of Workers for proxy operations. Quick deployments implemented on CF-Worker should be used for **experimental** and **temporary testing** purposes only, and not for long-term or high-traffic. - -OpenList is not responsible for any consequences resulting from the use - -::: -:::zh-CN :::danger CloudFlare已明确禁止使用Worker作为代理流量使用,对于在CF-Worker上实现的快速部署,应当时基于**实验性质**的**临时测试**,而非生产环境下使用长期、大流量使用。 @@ -40,33 +20,13 @@ OpenList对于使用该Worker造成的任何后果均不负责。 ::: -:::en -For OpenList Proxy, we provide two deployment methods: - -- cf-worker -- Binary File Deployment - -::: -:::zh-CN 对于OpenList Proxy,我们提供了两种部署方式 - cf-worker - 二进制文件部署 -::: - -### Cloudflare Worker { lang="en" } +### ~~科赋锐~~Cloudflare Worker -### ~~科赋锐~~Cloudflare Worker { lang="zh-CN" } - -:::en -:::tip -In the new version, environment-based configuration has been introduced. Please configure the environment variables as required after deployment. - -Do not use "/" at the end of the address. - -::: -:::zh-CN :::tip 在新版本中引入了基于环境的配置,请在部署完成后按照要求配置好环境变量。 @@ -74,35 +34,6 @@ Do not use "/" at the end of the address. ::: -:::en - -- Simple Worker Deployment Tutorial On the Cloudflare homepage, select "Workers and Pages", - then click "Create" and choose "Start from Hello World!". - After deployment, select "Edit Code", go to here, replace the code, and click "Deploy" again. - -- (Optional) Configure Domain Go to the Worker configuration page, click "Settings", then click "Add" next to Domains and Routes. Enter the configured subdomain. Use a CNAME record for the corresponding subdomain to point to the workers.dev domain. - -- Configure Environment Variables Go to the Worker configuration page, - click "Settings", then select "Add" next to Variables and Secrets. - Copy the following into the variable names: - - ```env - ADDRESS=https://your-openlist-server.com - TOKEN=your-api-token-here - WORKER_ADDRESS=https://your-worker-address - DISABLE_SIGN=false - ``` - -- ADDRESS is the address of your OpenList instance, only ports 443 and 80 are supported. - WORKER_ADDRESS is the address of the Worker. If you have bound a custom domain, use the custom domain. This is also the proxy address needed in OpenList. - If DISABLE_SIGN is set to true, Proxy will not verify signatures, and anyone who knows the file path and Proxy address can access the file. Please use with caution. - It is recommended to set TOKEN as a secret type. In OpenList, go to Settings → Others at the bottom; this token is long-term valid and has full permissions for OpenList. - -- CDN Configuration Suggestion: Keep ADDRESS and WORKER_ADDRESS configured as the origin addresses. - -::: -:::zh-CN - - 部署worker简易教程 在Cloudflare的主页选择“Workers 和 Pages”,然后点击“创建”,选择“从Hello World!开始” 部署完成后选择“编辑代码”,进入[这里](https://github.com/OpenListTeam/OpenList-Proxy/blob/main/openlist-proxy.js),将代码替换后再次点击“部署”。 @@ -130,49 +61,12 @@ Do not use "/" at the end of the address. - 对于CDN的配置建议:`ADDRESS`、`WORKER_ADDRESS`配置保留为源站地址。 -::: - -### Binary File Deployment { lang="en" } - -### 二进制文件部署 { lang="zh-CN" } - -:::en +### 二进制文件部署 -Download the [binary package](https://github.com/OpenListTeam/OpenList-Proxy/releases) and run the command `./openlist-proxy -help` to learn how to use it. -::: -:::zh-CN 下载[二进制包](https://github.com/OpenListTeam/OpenList-Proxy/releases)后,使用`./openlist-proxy -help`命令自行学习使用。 -::: - -## How OpenList Proxy Works { lang="en" } - -## OpenList Proxy 的工作原理 { lang="zh-CN" } +## OpenList Proxy 的工作原理 -:::en -OpenList Proxy works by proxying the OpenList API to isolate the download traffic of OpenList. Its working principle is as follows: - -```mermaid -sequenceDiagram - participant Client as Client - participant Proxy as Cloudflare Worker - participant OpenList as OpenList Server - participant Storage as File Storage - - Client->>Proxy: Download Request (with signature) - Proxy->>Proxy: Verify signature and expiration time - Proxy->>OpenList: Request file link (/api/fs/link) - OpenList-->>Proxy: Return real download address - Proxy->>Storage: Request file content - Storage-->>Proxy: Return file stream - Proxy-->>Client: Forward file content -``` - -Proxy verifies the signature and expiration time to ensure the legitimacy of the request. It then requests the file link from the OpenList server to obtain the real download address. After that, Proxy requests the file storage service for the file content and forwards it to the client. - -Proxy's signature can be disabled by setting the `DISABLE_SIGN` environment variable or flag. If signature verification is disabled, Proxy will not check the signature and expiration time, allowing anyone who knows the file path and Proxy address to bypass OpenList's own signature verification (which can be configured in the management interface) to access the file. Please use this feature with caution. -::: -:::zh-CN OpenList Proxy 通过对 OpenList 的 API 进行代理,来实现对 OpenList 的下载流量的隔离。其工作原理如下: ```mermaid @@ -194,4 +88,3 @@ sequenceDiagram Proxy 通过验证签名和过期时间,确保请求的合法性。然后,它向 OpenList 服务器请求文件链接,并获取真实的下载地址。接着,Proxy 请求文件存储服务获取文件内容,并将其转发给客户端。 Proxy的签名可以通过`DISABLE_SIGN`环境变量或flag来禁用,如果禁用签名,Proxy将不会验证签名和过期时间,这样任何人都可以通过知道文件路径和Proxy地址来绕过OpenList本身的签名验证(其本身的签名可以在管理界面配置)获取文件。请谨慎开启此功能。 -::: diff --git a/pages/ecosystem/official_worker/about.md b/pages/ecosystem/official_worker/about.md index 3ff06fb42..300138743 100644 --- a/pages/ecosystem/official_worker/about.md +++ b/pages/ecosystem/official_worker/about.md @@ -1,63 +1,26 @@ --- -title: - en: FAQ - zh-CN: 常见问题 categories: - ecosystem - eco_worker top: 971 --- -## FAQ { lang="en" } +# 常见问题 -## 常见问题 { lang="zh-CN" } - -:::en - -### The install wizard doesn't appear on first visit - -Make sure `ADMIN_PASS` is **not** set — when it is set, the worker auto-initializes and skips the wizard. Clear the variable and redeploy, then revisit the site. -::: - -:::zh-CN +## 常见问题 ### 首次访问没有出现安装向导 确认 `ADMIN_PASS` **未配置**——配置后 Worker 会自动初始化并跳过向导。清除该变量后重新部署,再次访问即可进入向导。 -::: --- -:::en - -### Cloudflare reports "unable to fetch repository content" - -You are trying to deploy directly from the upstream `OpenListTeam/OpenList-Worker` repository. Cloudflare Workers only allows deploying from repositories you own. [Fork](https://github.com/OpenListTeam/OpenList-Worker/fork) the project first, then connect your fork. -::: - -:::zh-CN - ### Cloudflare 提示"无法获取存储库内容" 你正在尝试直接从上游 `OpenListTeam/OpenList-Worker` 仓库部署。Cloudflare Workers 只允许部署你自己拥有的仓库。请先 [Fork](https://github.com/OpenListTeam/OpenList-Worker/fork) 项目,再连接你的 Fork 仓库。 -::: - --- -:::en - -### Settings are lost after redeployment / page refresh - -This usually means no persistent storage is configured. Check: - -1. `DB_DRIVER` is set (not `memory`) -2. The corresponding binding (KV / D1 / Blob) is correctly bound in the platform dashboard -3. For EdgeOne, the default `auto` driver should auto-detect Blob — if not, explicitly set `DB_DRIVER=blob` - ::: - -:::zh-CN - ### 重新部署 / 刷新页面后设置丢失 通常意味着没有配置持久化存储。检查: @@ -65,24 +28,9 @@ This usually means no persistent storage is configured. Check: 1. `DB_DRIVER` 已设置(不是 `memory`) 2. 对应绑定(KV / D1 / Blob)已在平台后台正确绑定 3. EdgeOne 默认的 `auto` 驱动应能自动探测 Blob——若不行,请显式设置 `DB_DRIVER=blob` - ::: --- -:::en - -### How do I reset the admin password? - -If you can still log in, go to **Management → Users** to change the password. - -If you are locked out: - -1. Set the `ADMIN_PASS` environment variable to a new password and redeploy. -2. After logging in, remove the variable and redeploy again to re-enable the install wizard on next cold start (or leave it set as a permanent password). - ::: - -:::zh-CN - ### 如何重置管理员密码? 如果你还能登录,前往 **管理 → 用户** 修改密码即可。 @@ -91,24 +39,9 @@ If you are locked out: 1. 将 `ADMIN_PASS` 环境变量设为新密码并重新部署。 2. 登录后删除该变量再次部署,下次冷启动时将重新启用安装向导(或保留该变量作为永久密码)。 - ::: --- -:::en - -### CORS errors when accessing the API from a custom domain - -Add `ALLOW_URLS` as an environment variable with a comma-separated list of allowed origins, e.g.: - -``` -ALLOW_URLS=https://your-domain.com,https://www.your-domain.com -``` - -::: - -:::zh-CN - ### 从自定义域名访问 API 时出现 CORS 错误 添加 `ALLOW_URLS` 环境变量,值为逗号分隔的允许来源列表,例如: @@ -117,24 +50,8 @@ ALLOW_URLS=https://your-domain.com,https://www.your-domain.com ALLOW_URLS=https://your-domain.com,https://www.your-domain.com ``` -::: - --- -:::en - -### `DB_FORMAT=sql` tables are not created automatically - -D1 tables are created via Drizzle migrations on first startup. Make sure: - -1. The D1 binding (`DB`) is correctly configured in `wrangler.toml` or the dashboard. -2. After binding, trigger a cold start by redeploying. - -If using **Automatic resource provisioning**, omit `database_id` from the D1 binding and Wrangler (>= 4.45.0) will create the database automatically on deploy. -::: - -:::zh-CN - ### `DB_FORMAT=sql` 表未自动创建 D1 表通过 Drizzle 迁移在首次启动时创建。请确认: @@ -143,23 +60,9 @@ D1 表通过 Drizzle 迁移在首次启动时创建。请确认: 2. 绑定后重新部署触发一次冷启动。 如果使用 **Automatic resource provisioning**,在 D1 绑定中省略 `database_id`,Wrangler(>= 4.45.0)会在部署时自动创建数据库。 -::: --- -:::en - -### How do I migrate data from the Go backend to OpenList Worker? - -Use `DB_FORMAT=sql` + `DB_DRIVER=d1` (or `mysql`) with the fixed `x_` table prefix. The TS Worker and the Go backend share the same table schema, so you can: - -1. Export the Go backend's SQLite database. -2. Import it into a Cloudflare D1 database via the Cloudflare dashboard or `wrangler d1 execute`. -3. Configure the Worker to point to the same D1 database. - ::: - -:::zh-CN - ### 如何将 Go 后端的数据迁移到 OpenList Worker? 使用固定的 `x_` 表名前缀,配合 `DB_FORMAT=sql` + `DB_DRIVER=d1`(或 `mysql`)。TS Worker 与 Go 后端共享相同的表 schema,因此: @@ -167,42 +70,18 @@ Use `DB_FORMAT=sql` + `DB_DRIVER=d1` (or `mysql`) with the fixed `x_` table pref 1. 导出 Go 后端的 SQLite 数据库。 2. 通过 Cloudflare 控制台或 `wrangler d1 execute` 将其导入 Cloudflare D1 数据库。 3. 配置 Worker 指向同一个 D1 数据库即可。 - ::: --- -:::en - -### ESA EdgeKV settings revert after a few seconds - -This is caused by EdgeKV's eventual consistency. The Worker implements a module-level cache with a 60-second TTL to mitigate this. If the issue persists, wait ~60 seconds for the cache to expire and the setting to propagate across nodes. -::: - -:::zh-CN - ### 阿里云 ESA EdgeKV 设置几秒后回滚 这是 EdgeKV 最终一致性导致的。Worker 内置了 60 秒 TTL 的模块级缓存来缓解此问题。如果问题持续,等待约 60 秒让缓存过期、设置同步到各节点即可。 -::: --- -:::en - -### Build fails with "pnpm: command not found" - -The deploy platform is using npm by default. Either: - -- Set the **install command** to `npm install --legacy-peer-deps` and **build command** to `npm run build`, or -- Enable pnpm in the platform settings (e.g. Cloudflare Workers → Framework preset → set Node version to 18+) - ::: - -:::zh-CN - ### 构建失败,提示 "pnpm: command not found" 部署平台默认使用 npm。可以: - 将 **安装命令** 改为 `npm install --legacy-peer-deps`,**构建命令** 改为 `npm run build`,或者 - 在平台设置中启用 pnpm(例如 Cloudflare Workers → Framework preset → 设置 Node 版本为 18+) - ::: diff --git a/pages/ecosystem/official_worker/basic.md b/pages/ecosystem/official_worker/basic.md index 1b9f08d76..24a3a131e 100644 --- a/pages/ecosystem/official_worker/basic.md +++ b/pages/ecosystem/official_worker/basic.md @@ -1,125 +1,51 @@ --- -title: - en: Architecture - zh-CN: 设计架构 categories: - ecosystem - eco_worker top: 978 --- -## Design Architecture { lang="en" } +# 设计架构 -## 设计架构 { lang="zh-CN" } +## 设计架构 -:::en -OpenList Worker is a Serverless-first rewrite of the OpenList Go backend in TypeScript. The system is divided into three layers: edge runtime, data access, and frontend static assets. -::: - -:::zh-CN OpenList Worker 是将 OpenList Go 后端以 TypeScript 重写的 Serverless 优先架构。系统分为边缘运行时、数据访问和前端静态资源三层。 -::: - -## Tech Stack { lang="en" } - -## 技术栈 { lang="zh-CN" } - -### Backend { lang="en" } - -### 后端 { lang="zh-CN" } - -:::en -| Component | Technology | Description | -| :-------- | :--------- | :---------- | -| HTTP framework | [Hono.js](https://hono.dev/) | Lightweight, edge-native web framework | -| Runtime | Cloudflare Workers / EdgeOne Functions / ESA | Edge compute platforms | -| Language | TypeScript | Fully typed, compiled via esbuild / Vite | -| ORM | Drizzle ORM | Type-safe SQL query builder for D1 / MySQL | -| Build tool | esbuild / Vite | Single-file Worker bundle | -::: - -:::zh-CN -| 组件 | 技术 | 说明 | -| :--- | :--- | :--- | -| HTTP 框架 | [Hono.js](https://hono.dev/) | 轻量级、边缘原生 Web 框架 | -| 运行时 | Cloudflare Workers / EdgeOne 云函数 / ESA | 边缘计算平台 | -| 语言 | TypeScript | 全类型,使用 esbuild / Vite 编译 | -| ORM | Drizzle ORM | 为 D1 / MySQL 提供类型安全的 SQL 查询构建器 | -| 构建工具 | esbuild / Vite | 单文件 Worker 产物 | -::: - -### Frontend { lang="en" } - -### 前端 { lang="zh-CN" } - -:::en -| Component | Technology | Description | -| :-------- | :--------- | :---------- | -| Framework | SolidJS + TypeScript | Reactive SPA frontend | -| UI library | Hope UI (@hope-ui/solid) | Component library | -| Build tool | Vite | Fast frontend build | -| Bundled with | Workers Static Assets | Served from the same origin as the API | -::: -:::zh-CN -| 组件 | 技术 | 说明 | -| :--- | :--- | :--- | -| 框架 | SolidJS + TypeScript | 响应式 SPA 前端 | -| UI 库 | Hope UI(@hope-ui/solid) | 组件库 | -| 构建工具 | Vite | 快速前端构建 | -| 与 Worker 同源 | Workers Static Assets | API 与前端同源部署,无跨域问题 | -::: +## 技术栈 -## Data Storage { lang="en" } +### 后端 -## 数据存储 { lang="zh-CN" } +| 组件 | 技术 | 说明 | +| :-------- | :---------------------------------------- | :------------------------------------------ | +| HTTP 框架 | [Hono.js](https://hono.dev/) | 轻量级、边缘原生 Web 框架 | +| 运行时 | Cloudflare Workers / EdgeOne 云函数 / ESA | 边缘计算平台 | +| 语言 | TypeScript | 全类型,使用 esbuild / Vite 编译 | +| ORM | Drizzle ORM | 为 D1 / MySQL 提供类型安全的 SQL 查询构建器 | +| 构建工具 | esbuild / Vite | 单文件 Worker 产物 | -### Storage Format (`DB_FORMAT`) { lang="en" } +### 前端 -### 存储格式(`DB_FORMAT`) { lang="zh-CN" } +| 组件 | 技术 | 说明 | +| :------------- | :------------------------ | :----------------------------- | +| 框架 | SolidJS + TypeScript | 响应式 SPA 前端 | +| UI 库 | Hope UI(@hope-ui/solid) | 组件库 | +| 构建工具 | Vite | 快速前端构建 | +| 与 Worker 同源 | Workers Static Assets | API 与前端同源部署,无跨域问题 | -:::en -The `DB_FORMAT` variable controls how data is serialized: +## 数据存储 -| Value | Description | Best for | -| :-------------- | :---------------------------------------------------- | :-------------------------------------- | -| `map` (default) | Whole object serialized as a single JSON value | KV / Blob storage | -| `key` | Per-key storage, one record per entity | KV with high read frequency | -| `sql` | Relational tables, identical schema to the Go backend | D1 / MySQL — enables Go ↔ TS migration | +### 存储格式(`DB_FORMAT`) -::: - -:::zh-CN `DB_FORMAT` 控制数据序列化方式: -| 值 | 说明 | 最适用场景 | -| :------------ | :--------------------------------- | :----------------------------- | -| `map`(默认) | 整对象序列化为单个 JSON 值 | KV / Blob 存储 | -| `key` | 分 key 存储,每实体一条记录 | 高频读写 KV | +| 值 | 说明 | 最适用场景 | +| :------------ | :--------------------------------- | :---------------------------- | +| `map`(默认) | 整对象序列化为单个 JSON 值 | KV / Blob 存储 | +| `key` | 分 key 存储,每实体一条记录 | 高频读写 KV | | `sql` | 关系表,与 Go 后端 schema 完全一致 | D1 / MySQL——支持 Go ↔ TS 迁移 | -::: - -### Storage Driver (`DB_DRIVER`) { lang="en" } - -### 存储驱动(`DB_DRIVER`) { lang="zh-CN" } - -:::en -The `DB_DRIVER` variable selects the physical storage backend: - -| Value | Platform | Description | -| :--------------- | :----------------- | :------------------------------------------ | -| `auto` (default) | Universal | Auto-detect: blob → cfkv → kv → d1 → memory | -| `blob` | EdgeOne / ESA | EdgeOne Blob or Alibaba ESA Blob | -| `cfkv` | Universal | Cloudflare KV via REST API (cross-platform) | -| `kv` | Cloudflare Workers | Cloudflare KV binding | -| `d1` | Cloudflare Workers | Cloudflare D1 (SQLite) | -| `do` | Cloudflare Workers | Durable Objects (strong consistency) | -| `mysql` | Node.js container | External MySQL / MariaDB | +### 存储驱动(`DB_DRIVER`) -::: - -:::zh-CN `DB_DRIVER` 选择物理存储后端: | 值 | 平台 | 说明 | @@ -132,28 +58,8 @@ The `DB_DRIVER` variable selects the physical storage backend: | `do` | Cloudflare Workers | Durable Objects(强一致性) | | `mysql` | Node.js 容器 | 外部 MySQL / MariaDB | -::: - -### SQL Table Alignment with Go Backend { lang="en" } - -### 与 Go 后端的 SQL 表对齐 { lang="zh-CN" } - -:::en -When `DB_FORMAT = "sql"`, the TS Worker uses the same table names and schema as the Go backend (GORM, default prefix `x_`), so the two backends can share the same physical database: +### 与 Go 后端的 SQL 表对齐 -| Go struct | Table name | -| :------------ | :---------------- | -| `SettingItem` | `x_setting_items` | -| `SharingDB` | `x_sharing_dbs` | -| `Storage` | `x_storages` | -| `User` | `x_users` | -| `Meta` | `x_metas` | -| (TS only) | `x_plugins` | - -The prefix is fixed to `x_`, matching the Go backend default. -::: - -:::zh-CN 当 `DB_FORMAT = "sql"` 时,TS Worker 使用与 Go 后端(GORM,默认前缀 `x_`)相同的表名与 schema,两个后端可共享同一物理数据库: | Go 结构体 | 表名 | @@ -166,35 +72,8 @@ The prefix is fixed to `x_`, matching the Go backend default. | (仅 TS) | `x_plugins` | 表名前缀固定为 `x_`,与 Go 后端默认值一致。 -::: - -## Project Structure { lang="en" } - -## 项目结构 { lang="zh-CN" } -:::en - -``` -OpenList-Worker/ -├── src/ -│ ├── backend/ # Hono.js Worker entry & backend logic -│ │ ├── worker.ts # Cloudflare Workers entry -│ │ ├── drivers/ # Storage driver implementations (kv / d1 / blob / mysql …) -│ │ ├── server/ # Route registrations & middleware -│ │ ├── pkg/ # Shared utilities & helpers -│ │ └── internal/ # Core business logic (auth, storage, meta, …) -│ └── frontend/ # Built-in frontend (SolidJS + Vite) -├── dist/ # Build output (Worker bundle + frontend assets) -├── esa-entry.ts # Alibaba Cloud ESA entry -├── wrangler.toml # Cloudflare Workers configuration -├── esa.jsonc # Alibaba Cloud ESA configuration -├── edgeone.json # EdgeOne schedules configuration -└── package.json -``` - -::: - -:::zh-CN +## 项目结构 ``` OpenList-Worker/ @@ -214,26 +93,11 @@ OpenList-Worker/ └── package.json ``` -::: - -## Supported Platforms { lang="en" } - -## 支持平台 { lang="zh-CN" } - -:::en -| Platform | Entry | Persistence | Notes | -| :------- | :---- | :---------- | :---- | -| Cloudflare Workers | `worker.ts` | D1 / KV / DO | One-click deploy supported | -| Tencent Cloud EdgeOne | `worker.ts` | Blob / KV | One-click deploy supported | -| Alibaba Cloud ESA | `esa-entry.ts` | EdgeKV | Manual build & deploy | -| Node.js container | `worker.ts` (with adapter) | MySQL | Self-hosted | -::: - -:::zh-CN -| 平台 | 入口 | 持久化 | 备注 | -| :--- | :--- | :----- | :--- | -| Cloudflare Workers | `worker.ts` | D1 / KV / DO | 支持一键部署 | -| 腾讯云 EdgeOne | `worker.ts` | Blob / KV | 支持一键部署 | -| 阿里云 ESA | `esa-entry.ts` | EdgeKV | 手动构建部署 | -| Node.js 容器 | `worker.ts`(带适配层) | MySQL | 自托管 | -::: +## 支持平台 + +| 平台 | 入口 | 持久化 | 备注 | +| :----------------- | :---------------------- | :----------- | :----------- | +| Cloudflare Workers | `worker.ts` | D1 / KV / DO | 支持一键部署 | +| 腾讯云 EdgeOne | `worker.ts` | Blob / KV | 支持一键部署 | +| 阿里云 ESA | `esa-entry.ts` | EdgeKV | 手动构建部署 | +| Node.js 容器 | `worker.ts`(带适配层) | MySQL | 自托管 | diff --git a/pages/ecosystem/official_worker/guide.md b/pages/ecosystem/official_worker/guide.md index 154a09c95..d8db196a4 100644 --- a/pages/ecosystem/official_worker/guide.md +++ b/pages/ecosystem/official_worker/guide.md @@ -1,113 +1,38 @@ --- -title: - en: Deployment - zh-CN: 部署教程 categories: - ecosystem - eco_worker top: 977 --- -## How to Deploy { lang="en" } +# 部署教程 -## 部署方法 { lang="zh-CN" } +## 部署方法 -:::: en -For a detailed, step-by-step deployment guide (Cloudflare Workers / EdgeOne / ESA), see [OpenList Worker 部署指南](/guide/installation/worker). -:::: - -:::: zh-CN 详细的分步部署指南(Cloudflare Workers / EdgeOne / ESA)请参阅 [OpenList Worker 部署指南](/guide/installation/worker)。 -:::: - -### One-click Deploy { lang="en" } -### 一键部署 { lang="zh-CN" } +### 一键部署 | EdgeOne 国际站 | EdgeOne 中国站 | Cloudflare Workers | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://console.cloud.tencent.com/edgeone/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/OpenListTeam/OpenList-Worker) | -:::: en - -> **Note**: If Cloudflare reports "unable to fetch repository content", [Fork](https://github.com/OpenListTeam/OpenList-Worker/fork) the project first, then deploy via the GitHub repository connection. -> :::: - -:::: zh-CN - > **注意**:若 Cloudflare 提示"无法获取存储库内容",请先 [Fork](https://github.com/OpenListTeam/OpenList-Worker/fork) 本项目,再通过连接到 GitHub 仓库功能部署。 -> :::: -:::: en -For platform-specific step-by-step guides, see: - -- [Cloudflare Workers](./guide_cfw) -- [Tencent Cloud EdgeOne](./guide_eom) -- [Alibaba Cloud ESA](./guide_esa) - :::: - -:::: zh-CN 各平台的分步部署教程,请参阅: - [Cloudflare Workers](./guide_cfw) - [腾讯云 EdgeOne](./guide_eom) - [阿里云 ESA](./guide_esa) - :::: - -### Initialization { lang="en" } -### 部署后初始化 { lang="zh-CN" } +### 部署后初始化 -:::: en -::: tip -After deployment, the first visit to the site automatically enters an **install wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. -::: -:::: - -:::: zh-CN ::: tip 部署完成后,首次访问站点会自动进入**安装向导**,在浏览器中设置管理员账号与密码即可完成初始化,无需预先配置 `ADMIN_PASS`。 ::: -:::: - -### Local Development { lang="en" } - -### 本地开发 { lang="zh-CN" } - -:::: en -**Prerequisites** - -- Node.js 18+ (pnpm recommended) -- A Cloudflare account (for deploying to Workers) - -**Local development** -```bash -# 1. Install dependencies -pnpm install - -# 2. Configure wrangler.toml (fill in JWT_SECRET, KV/D1 bindings) - -# 3. Start the dev server (auto fetch the official frontend and run the Worker) -pnpm run dev:unified - -# or run the Worker only (frontend built separately) -pnpm run dev:worker -``` - -**Production deploy** +### 本地开发 -```bash -# One-click deploy: ensure KV namespace exists → fetch official frontend → deploy to Cloudflare Workers -pnpm run deploy - -# or deploy the Worker directly (skip KV check and frontend build) -pnpm run deploy:worker -``` - -:::: - -:::: zh-CN **前置要求** - Node.js 18+(推荐使用 pnpm) @@ -137,5 +62,3 @@ pnpm run deploy # 或直接部署 Worker(跳过 KV 检查与前端构建) pnpm run deploy:worker ``` - -:::: diff --git a/pages/ecosystem/official_worker/guide_cfw.md b/pages/ecosystem/official_worker/guide_cfw.md index 0f4a9b76d..98687c571 100644 --- a/pages/ecosystem/official_worker/guide_cfw.md +++ b/pages/ecosystem/official_worker/guide_cfw.md @@ -1,117 +1,13 @@ --- -title: - en: Deploy to Cloudflare Workers - zh-CN: 部署教程 - Cloudflare Workers categories: - ecosystem - eco_worker top: 967 --- -## Deploy to Cloudflare Workers { lang="en" } +# 部署教程 - Cloudflare Workers -## 部署到 Cloudflare Workers { lang="zh-CN" } - -:::: en - -### Prerequisites - -- A [Cloudflare](https://dash.cloudflare.com/) account -- A [GitHub](https://github.com/) account (for connecting the repository) -- Node.js 18+ and pnpm (only required for local / Wrangler deploy) - -### Deploy methods - -You have two options to deploy to Cloudflare Workers: - -1. **One-click deploy** — click the **Deploy to Cloudflare Workers** button above. -2. **Manual create via GitHub** — follow the steps below. - -### Step 1: Create an application & connect GitHub - -Open the Cloudflare Workers dashboard, click **Create application** in the top-right, and choose **Connect to GitHub**. Cloudflare will prompt you to authorize access to your GitHub account — click **Authorize** and select the account (or organization) that contains your fork. - -![Create an application — connect GitHub](/img/worker/create_app1.png) - -### Step 2: Select the repository - -Fork this project to your own GitHub account first, then select your forked Worker repository when creating the application. - -::: tip -If you deploy directly using the upstream `OpenListTeam/OpenList-Worker` repository and Cloudflare reports "unable to fetch repository content", fork the project first and select your fork instead. -::: - -![Select the forked repository](/img/worker/create_app2.png) - -### Step 3: Create the application - -Click **Create**. Keep the default build parameters and commands: - -- **Framework preset**: None / Workers -- **Build command**: `pnpm run build` -- **Deploy command**: `npx wrangler deploy` -- **Production branch**: `main` - -Cloudflare will build the Worker and deploy it to a `*.workers.dev` subdomain. - -![Keep default build parameters](/img/worker/create_app3.png) - -### Step 4: Configure environment variables - -Enter the Worker project you just created, open **Settings → Variables and secrets** (or **Runtime variables and secrets**), and add the environment variables. - -![Add environment variables](/img/worker/create_app4.png) - -The required variables (`DB_FORMAT`, `DB_DRIVER`) and their optional combinations are described in the [Environment Variables](#environment-variables) section at the end of this page. - -### Step 5: Bind the storage binding - -If you selected KV or D1 as the driver, you need to bind the corresponding binding in **Settings → Bindings**: - -| Type | Variable name | -| ---- | ------------- | -| `d1` | `DB` | -| `kv` | `KV` | -| `do` | `DO` | - -![Bind the KV / D1 / DO binding](/img/worker/create_app5.png) - -::: tip Automatic D1 provisioning -For D1, you can enable Cloudflare's **Automatic resource provisioning**: omit `database_id` in the D1 binding, and Wrangler (>= 4.45.0) auto-creates a D1 database with the same name and writes back the ID on deploy. -::: - -### Step 6: Bind a custom domain - -Add your own domain in **Settings → Domains and Routes**, then create a CNAME record for the subdomain pointing to your `*.workers.dev` domain. - -![Bind a custom domain](/img/worker/create_app6.png) - -### After deployment - -::: tip -After deployment, the first visit to the site automatically enters an **install wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. -::: - -### Local / Wrangler deploy (alternative) - -If you prefer deploying via the command line: - -```bash -# 1. Clone and install dependencies -git clone https://github.com/OpenListTeam/OpenList-Worker.git -cd OpenList-Worker -pnpm install - -# 2. Configure wrangler.toml (JWT_SECRET, KV / D1 bindings) - -# 3. Deploy to Cloudflare Workers -pnpm run deploy -# or: pnpm run deploy:worker (skip frontend build) -``` - -:::: - -:::: zh-CN +## 部署到 Cloudflare Workers ### 前置要求 @@ -208,12 +104,4 @@ pnpm run deploy # 或:pnpm run deploy:worker(跳过前端构建) ``` -:::: - -::: en -For a full list of environment variables and recommended configurations, see [Environment Variables](./guide_env). -::: - -::: zh-CN 完整的环境变量说明与推荐配置组合,请参阅 [配置变量](./guide_env)。 -::: diff --git a/pages/ecosystem/official_worker/guide_env.md b/pages/ecosystem/official_worker/guide_env.md index 7e325379d..8371009b8 100644 --- a/pages/ecosystem/official_worker/guide_env.md +++ b/pages/ecosystem/official_worker/guide_env.md @@ -1,107 +1,13 @@ --- -title: - en: Environment Variables - zh-CN: 环境变量 categories: - ecosystem - eco_worker top: 972 --- -## Environment Variables { lang="en" } +# 环境变量 -## 配置变量 { lang="zh-CN" } - -:::en - -### Required variables - -The following parameters are required: - -| Parameter | Optional values | Description | -| ----------- | ----------------------------------------------------------------- | ----------------------- | -| `DB_FORMAT` | `map` (default) / `key` / `sql` | Data persistence format | -| `DB_DRIVER` | `auto` (default) / `blob` / `cfkv` / `kv` / `d1` / `do` / `mysql` | Storage location | - -#### Security variables - -In addition to `DB_FORMAT` and `DB_DRIVER`, it is strongly recommended to configure the following security-related variables: - -| Variable | Required | Description | -| ------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `JWT_SECRET` | Recommended | JWT signing key (≥16 chars), also used for data encryption and cron task auth. Auto-generated and persisted to KV when unset | -| `ADMIN_PASS` | Optional | Skip the install wizard and auto-initialize the admin with this password | -| `ALLOW_URLS` | Optional | Comma-separated CORS allowlist | - -#### Other optional variables - -| Variable | Default | Description | -| ------------ | ---------- | ---------------------------------------------------------------- | -| `MAX_UPLOAD` | `26214400` | Max size (bytes) for a whole upload (`/put`, `/form`) | -| `MAX_UPPART` | `16777216` | Max size (bytes) per chunk in multipart upload | -| `ASSET_URLS` | — | Frontend asset CDN base URL, supports the `$version` placeholder | -| `ALLOW_SEED` | — | Allowlist of hosts permitted as seed-data sources | - -### Storage format differences - -| `DB_FORMAT` | Description | -| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| `map` | All data stored as a single JSON. Reads the whole object on every access — lower performance but best compatibility. | -| `key` | Each record stored as a Key-Value pair, read on demand — decent compatibility and performance, but no indexing. | -| `sql` | Data stored as SQL tables, fully identical to the Go backend. Supports indexing and full features, but multiple reads take longer. | - -### Storage driver differences - -| `DB_DRIVER` | Supported platform | Description | -| ----------- | ------------------ | -------------------------------------------------------------------------- | -| `auto` | Universal | Auto-detect, priority: blob → cfkv → kv → d1 → memory | -| `blob` | EdgeOne / ESA | Persistence provided by EdgeOne or Alibaba ESA Blob, free | -| `cfkv` | Universal | Cloudflare KV REST API, for remote access on platforms without persistence | -| `kv` | CF / EO / ESA | KV database, fast and free | -| `d1` | CF | D1 database, Cloudflare only | -| `do` | CF | Durable Objects, Cloudflare only | -| `mysql` | Universal | Connect to your own MySQL database | - -### Valid combinations - -| `DB_FORMAT` | `DB_DRIVER` | Description | -| ----------- | ----------- | --------------------------------------------------------------------- | -| `map` | `auto` | JSON storage, auto-selects blob / kv / d1 based on the platform | -| `map` | `blob` | JSON storage via EdgeOne / ESA Blob (recommended for those platforms) | -| `map` | `cfkv` | JSON storage via remote CF KV outside CF Workers | -| `map` | `kv` | JSON storage via KV on CF Workers (recommended) | -| `map` | `d1` | JSON storage via D1 on CF Workers (not recommended) | -| `map` | `do` | JSON storage via DO on CF Workers (not recommended, may incur cost) | -| `key` | `kv` | Key-Value storage via KV on CF Workers (recommended) | -| `key` | `d1` | Key-Value storage via D1 on CF Workers (recommended) | -| `key` | `mysql` | Key-Value storage via MySQL on CF Workers (not recommended) | -| `sql` | `d1` | SQL-Table storage via D1 on CF Workers (recommended) | -| `sql` | `mysql` | SQL-Table storage via MySQL | - -### Remote database configuration - -If you bound `cfkv`, set the following variables: - -| Variable | Description | -| ------------ | ----------------------------------- | -| `CF_ACCOUNT` | Cloudflare account ID | -| `CF_KV_UUID` | Cloudflare KV namespace ID | -| `CF_API_KEY` | Cloudflare API token with KV access | - -If you bound MySQL, set the following variables: - -| Variable | Description | -| ------------ | ------------------------------------------------------------------------------------------------ | -| `MYSQL_URL` | Connection string, e.g. `mysql://user:pass@host:3306/db`. When set, the fields below are ignored | -| `MYSQL_HOST` | Database host | -| `MYSQL_PORT` | Database port (`3306`) | -| `MYSQL_USER` | Database user | -| `MYSQL_PASS` | Database password | -| `MYSQL_NAME` | Database name | - -::: - -:::zh-CN +## 配置变量 ### 必选变量 @@ -187,5 +93,3 @@ If you bound MySQL, set the following variables: | `MYSQL_USER` | 数据库用户 | | `MYSQL_PASS` | 数据库密码 | | `MYSQL_NAME` | 数据库名称 | - -::: diff --git a/pages/ecosystem/official_worker/guide_eom.md b/pages/ecosystem/official_worker/guide_eom.md index 9c4d39fe5..ce328c37a 100644 --- a/pages/ecosystem/official_worker/guide_eom.md +++ b/pages/ecosystem/official_worker/guide_eom.md @@ -1,85 +1,13 @@ --- -title: - en: Deploy to Tencent EdgeOne - zh-CN: 部署教程 - Tencent EdgeOne categories: - ecosystem - eco_worker top: 966 --- -## Deploy to EdgeOne { lang="en" } +# 部署教程 - Tencent EdgeOne -## 部署到 EdgeOne { lang="zh-CN" } - -:::: en - -### Prerequisites - -- A [Tencent Cloud EdgeOne](https://console.edgeone.ai/makers) account -- A GitHub account (for connecting the repository) - -### Step 1: Deploy the application - -Click the **EdgeOne** deploy button above and choose the international or China site: - -- [International console](https://console.edgeone.ai/makers) -- [China console](https://console.cloud.tencent.com/edgeone/makers) - -### Step 2: Set environment variables - -Click **New application**, select the forked repository, configure the variables, then click **Start deployment**: - -![Select the forked repository and set variables](/img/worker/edgeone_ui1.png) - -Required variables (`DB_FORMAT`, `DB_DRIVER`) are described in the [Environment Variables](#environment-variables) section at the end of this page. - -::: tip Persistence -EdgeOne Makers uses `@edgeone/pages-blob` for persistence. The default `auto` driver auto-detects Blob, so no extra configuration is needed. You can also explicitly set: - -- `DB_DRIVER = "blob"` with `DB_FORMAT = "map"` (JSON storage) -- `DB_DRIVER = "kv"` with `DB_FORMAT = "key"` (Key-Value storage) - ::: - -### Step 3: Bind KV storage - -If you use KV storage in the environment variables, you must bind the `KV` variable to your namespace under **Storage → KV Storage**: - -![Bind the KV namespace](/img/worker/edgeone_ui3.png) - -### Step 4: Configure a custom domain - -After deployment, open **Domain management**, add a custom domain, then set up the CNAME record as required and enable SSL: - -![Add a custom domain and enable SSL](/img/worker/edgeone_ui2.png) - -### Step 5: Scheduled tasks - -EdgeOne supports scheduled refresh via `edgeone.json`. Set `cron_secret` in the payload to your `JWT_SECRET` value and configure the schedule: - -```jsonc -{ - "schedules": [ - { - "name": "token-refresh", - "cron": "0 2 * * *", - "path": "/api/task/refresh", - "method": "POST", - "payload": { "cron_secret": "" }, - "timezone": "Asia/Shanghai", - }, - ], -} -``` - -### After deployment - -::: tip -After deployment, the first visit to the site automatically enters an **install wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. -::: -:::: - -:::: zh-CN +## 部署到 EdgeOne ### 前置要求 @@ -144,12 +72,5 @@ EdgeOne 通过 `edgeone.json` 支持定时刷新。将 payload 中的 `cron_secr ::: tip 部署完成后,首次访问站点会自动进入**安装向导**,在浏览器中设置管理员账号与密码即可完成初始化,无需预先配置 `ADMIN_PASS`。 ::: -:::: -::: en -For a full list of environment variables and recommended configurations, see [Environment Variables](./guide_env). -::: - -::: zh-CN 完整的环境变量说明与推荐配置组合,请参阅 [配置变量](./guide_env)。 -::: diff --git a/pages/ecosystem/official_worker/guide_esa.md b/pages/ecosystem/official_worker/guide_esa.md index d9a8ad934..ee0704a06 100644 --- a/pages/ecosystem/official_worker/guide_esa.md +++ b/pages/ecosystem/official_worker/guide_esa.md @@ -1,52 +1,14 @@ --- -title: - en: Deploy to Alibaba Cloud ESA - zh-CN: 部署教程 - Alibaba Cloud ESA categories: - ecosystem - eco_worker top: 965 --- -## Deploy to Alibaba Cloud ESA { lang="en" } +# 部署教程 - Alibaba Cloud ESA -## 部署到阿里云 ESA { lang="zh-CN" } +## 部署到阿里云 ESA -:::: en -OpenList Worker ships a dedicated ESA edge function entry (`esa-entry.ts`), which adapts Alibaba Cloud EdgeKV into the project's KV interface. - -### Build & deploy - -```bash -# 1. Install dependencies -pnpm install - -# 2. Build (produces dist/esa-entry.js) -pnpm run build -``` - -`esa.jsonc` defines the edge function entry, install/build commands, and the static assets directory: - -```jsonc -{ - "name": "openlist", - "entry": "./dist/esa-entry.js", - "installCommand": "pnpm install --no-frozen-lockfile", - "buildCommand": "pnpm run build", - "assets": { "directory": "./dist" }, -} -``` - -### KV namespace - -Configure the EdgeKV namespace via the `KV_NAMESPACE` environment variable (default `openlist`). - -:::tip -ESA EdgeKV is eventually consistent. The entry implements a module-level TTL cache (60s) to avoid "saved settings revert after refresh" caused by cross-node sync delay. -::: -:::: - -:::: zh-CN OpenList Worker 内置了专用的 ESA 边缘函数入口(`esa-entry.ts`),将阿里云 EdgeKV 适配为项目的 KV 接口。 ### 部署应用 @@ -78,12 +40,5 @@ pnpm run build :::tip ESA EdgeKV 是最终一致性的。入口实现了带 TTL(60 秒)的模块级缓存,避免跨节点同步延迟导致的「保存设置后刷新复原」问题。 ::: -:::: -:::en -For a full list of environment variables and recommended configurations, see [Environment Variables](./guide_env). -::: - -:::zh-CN 完整的环境变量说明与推荐配置组合,请参阅 [配置变量](./guide_env)。 -::: diff --git a/pages/ecosystem/official_worker/index.md b/pages/ecosystem/official_worker/index.md index cc71ffed0..46e31cea8 100644 --- a/pages/ecosystem/official_worker/index.md +++ b/pages/ecosystem/official_worker/index.md @@ -1,30 +1,16 @@ --- -title: - en: Intro - zh-CN: 项目介绍 categories: - ecosystem - eco_worker top: 979 --- -## What is OpenList Worker { lang="en" } +# 项目介绍 -## OpenList Worker 是什么 { lang="zh-CN" } +## OpenList Worker 是什么 ### [OpenListTeam/OpenList-Worker](https://github.com/OpenListTeam/OpenList-Worker) -:::en -[**OpenList Worker**](https://github.com/OpenListTeam/OpenList-Worker) (repo `OpenList-TSWorker`) is the official TypeScript + Serverless port of [OpenList](https://github.com/OpenListTeam/OpenList). The Go backend is rewritten as a TypeScript service running on edge platforms (Cloudflare Workers / EdgeOne Cloud Function / Alibaba Cloud ESA), while the frontend keeps the same interface and interaction as the official OpenList frontend. - -- **Backend**: Hono.js, runs on Cloudflare Workers / EdgeOne Functions / Alibaba Cloud ESA -- **Frontend**: SolidJS + TypeScript, Hope UI, built with Vite -- **Database**: Cloudflare D1 (SQLite), MySQL / MariaDB -- **Cache**: Cloudflare KV / EdgeOne Blob / ESA EdgeKV (optional) -- **License**: AGPL-3.0 - ::: - -:::zh-CN [**OpenList Worker**](https://github.com/OpenListTeam/OpenList-Worker)(仓库 `OpenList-TSWorker`)是官方 [OpenList](https://github.com/OpenListTeam/OpenList) 项目的 TypeScript + Serverless 架构移植版。后端由 Go 重写为运行于边缘平台(Cloudflare Workers / EdgeOne 云函数 / 阿里云 ESA)上的 TypeScript 服务,前端保持与官方 OpenList 前端一致的界面与交互。 - **后端**:Hono.js,运行于 Cloudflare Workers / EdgeOne 云函数 / 阿里云 ESA @@ -32,30 +18,11 @@ top: 979 - **数据库**:Cloudflare D1(SQLite)、MySQL / MariaDB - **缓存**:Cloudflare KV / EdgeOne Blob / ESA EdgeKV(可选) - **许可证**:AGPL-3.0 - ::: - -## Features { lang="en" } - -## 功能特性 { lang="zh-CN" } - -### Storage Aggregation { lang="en" } - -### 存储聚合 { lang="zh-CN" } - -:::en -Built-in **81 storage drivers** to mount various storage backends out of the box: -- **Domestic drives**: Aliyundrive (Open Platform / share), Quark (Open Platform / UC TV), Baidu (album), 115 (Open Platform / share), 123 (Open Platform / share), Tianyi Cloud, China Mobile Cloud, Xunlei, Tencent Weiyun, Lanzou, PikPak (share), Doubao, Teambition, WPS, Alidoc, etc. -- **International drives**: Google Drive (album), OneDrive, Dropbox, MEGA, MediaFire, Proton Drive, Yandex Disk, TeraBox, etc. -- **Object storage**: S3-compatible (AWS/OSS/COS/MinIO), UPYUN USS, Azure Blob, WebDAV, FTP, SFTP, SMB, IPFS, etc. -- **Code hosting**: GitHub, GitHub Releases, CNB Releases -- **Drive programs**: OpenList (share), AList V3, Cloudreve V3/V4, Kodbox, Seafile, Teldrive, Febbox, etc. -- **Others**: NetEase Cloud Music, Misskey, Emby, Cloudflare image bed, etc. +## 功能特性 -In addition to the real storages above, virtual/functional drivers such as `Local`, `Alias`, `UrlTree`, `AutoIndex`, `Strm`, `Crypt`, `Virtual`, `Chunk` are provided for local mount, address alias, URL lists, encrypted storage and chunking scenarios. -::: +### 存储聚合 -:::zh-CN 内置 **81 个存储驱动**,开箱即用地挂载各类存储后端: - **国内网盘**:阿里云盘(开放平台/分享)、夸克网盘(开放平台/UC TV 版)、百度网盘(相册)、115 网盘(开放平台/分享)、123 云盘(开放平台/分享)、天翼云盘、中国移动云盘、迅雷云盘、腾讯微云、蓝奏云、PikPak(分享)、豆包网盘、Teambition 网盘、WPS 网盘、阿里文档等 @@ -66,24 +33,8 @@ In addition to the real storages above, virtual/functional drivers such as `Loca - **其他驱动**:网易云音乐、Misskey、Emby、Cloudflare 图床等 除上述真实存储外,还提供 `Local`、`Alias`、`UrlTree`、`AutoIndex`、`Strm`、`Crypt`、`Virtual`、`Chunk` 等虚拟/功能型驱动,可用于本地挂载、地址别名、URL 列表、加密存储与分片等场景。 -::: -### Core Capabilities { lang="en" } - -### 核心能力 { lang="zh-CN" } - -:::en - -- **File browsing**: unified directory tree browsing with online preview for images, videos, audio, documents, code, archives, etc. -- **Upload & download**: cross-storage upload, batch download, streaming transfer and direct-link redirect. -- **File sharing**: generate share links with expiration, password and permission control; support anonymous access and directory sharing. -- **Full-text search**: quickly search files in indexed storages. -- **Offline tasks**: background task queue for batch operations and async processing. -- **External interfaces**: expose aggregated storage via WebDAV or S3-compatible protocol for mounting into third-party tools. -- **MCP service**: provide a Model Context Protocol endpoint that can be integrated and called by AI assistants and other clients. - ::: - -:::zh-CN +### 核心能力 - **文件浏览**:统一的目录树浏览,支持图片、视频、音频、文档、代码、压缩包等格式在线预览。 - **上传下载**:跨存储的上传、批量下载、流式传输与直链跳转。 @@ -92,82 +43,31 @@ In addition to the real storages above, virtual/functional drivers such as `Loca - **离线任务**:后台任务队列,支持批量操作与异步处理。 - **外部接口**:将聚合存储以 WebDAV 或 S3 兼容协议对外暴露,便于挂载到第三方工具。 - **MCP 服务**:提供 Model Context Protocol 端点,可被 AI 助手等客户端集成调用。 - ::: - -### Permission Management { lang="en" } - -### 权限管理 { lang="zh-CN" } -:::en - -- **Access control**: role-based access control (RBAC), supporting user groups, directory-level read/write permissions and quotas. -- **Authentication**: built-in account/password, TOTP verification, WebAuthn/FIDO login, SSO single sign-on and LDAP directory authentication. -- **Security hardening**: JWT sessions, CSRF protection, clickjacking protection (X-Frame-Options), Content Security Policy (CSP). -- **Health checks**: `/health` liveness probe and `/healthz` readiness probe for monitoring and alerting. - ::: - -:::zh-CN +### 权限管理 - **权限管理**:基于角色的访问控制(RBAC),支持用户分组、目录级读写权限与配额。 - **认证方式**:内置账号密码,支持 TOTP 验证、WebAuthn/FIDO 登录、SSO 单点登录与 LDAP 目录认证。 - **安全加固**:JWT 会话、CSRF 防护、点击劫持防护(X-Frame-Options)、内容安全策略(CSP)。 - **健康检查**:提供 `/health` 存活探针与 `/healthz` 就绪探针,可用于监控与告警。 - ::: - -### Supported Platforms { lang="en" } - -### 支持平台 { lang="zh-CN" } - -:::en - -- **Runtime platforms**: Cloudflare Workers, Tencent Cloud EdgeOne Functions, Alibaba Cloud ESA. -- **Data storage**: Cloudflare D1 (SQLite) as primary, with external MySQL / MariaDB support. -- **Persistent cache**: Cloudflare KV / EdgeOne Blob / ESA EdgeKV (optional), used for configuration persistence and caching. -- **One-click deploy**: one-click deploy buttons + initialization for Cloudflare Workers, EdgeOne and Alibaba Cloud ESA. - ::: -:::zh-CN +### 支持平台 - **运行平台**:Cloudflare Workers、腾讯云 EdgeOne 云函数、阿里云 ESA。 - **数据存储**:Cloudflare D1(SQLite)为主,支持外部 MySQL / MariaDB。 - **持久缓存**:Cloudflare KV / EdgeOne Blob / ESA EdgeKV(可选),用于配置持久化与缓存。 - **一键部署**:支持 Cloudflare Workers、EdgeOne、阿里云 ESA 的一键部署按钮 + 初始化。 - ::: -## Documentation { lang="en" } +## 文档导航 -## 文档导航 { lang="zh-CN" } - -:::en - -- [Design Architecture](./architecture) — Tech stack, data storage backend and supported platforms -- [How to Deploy](./guide) — One-click deploy overview and local development - - [Cloudflare Workers](./guide_cfw) — Step-by-step Cloudflare Workers deployment (with screenshots) - - [EdgeOne](./guide_eom) — Tencent Cloud EdgeOne deployment - - [Alibaba Cloud ESA](./guide_esa) — Alibaba Cloud ESA deployment -- [Environment Variables](./guide_env) — `DB_FORMAT`, `DB_DRIVER` and all runtime variables -- [FAQ](./faq) — Common issues and troubleshooting - ::: - -:::zh-CN - -- [设计架构](./architecture) — 技术栈、数据存储后端与支持平台 +- [设计架构](./basic) — 技术栈、数据存储后端与支持平台 - [部署方法](./guide) — 一键部署入口与本地开发 - [Cloudflare Workers](./guide_cfw) — 分步 Cloudflare Workers 部署教程(含截图) - [EdgeOne](./guide_eom) — 腾讯云 EdgeOne 部署 - [阿里云 ESA](./guide_esa) — 阿里云 ESA 部署 - [配置变量](./guide_env) — `DB_FORMAT`、`DB_DRIVER` 及所有运行时变量 -- [常见问题](./faq) — 常见问题与排查 - ::: - -## License { lang="en" } - -## 许可证 { lang="zh-CN" } +- [常见问题](./about) — 常见问题与排查 -:::en -`OpenList` is open-source software licensed under [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.txt). -::: +## 许可证 -:::zh-CN `OpenList` 是基于 [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.txt) 许可证的开源软件。 -::: diff --git a/pages/en/api/apidocs.md b/pages/en/api/apidocs.md new file mode 100644 index 000000000..1a21d63aa --- /dev/null +++ b/pages/en/api/apidocs.md @@ -0,0 +1,14 @@ +--- +top: 10 +categories: + - api +--- + +# API Documentation + +Visit [fox.oplist.org](https://fox.oplist.org) to view and interact with our API documentation online. Live debugging is supported. + +All contents, endpoints, and code provided by this API are licensed under the [GNU Affero General Public License v3 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html). + +![apifox](/img/api/apifox-light.png#light) +![apifox](/img/api/apifox-dark.png#dark) diff --git a/pages/en/api/index.md b/pages/en/api/index.md new file mode 100644 index 000000000..e69de29bb diff --git a/pages/en/configuration/configuration.md b/pages/en/configuration/configuration.md new file mode 100644 index 000000000..7119e368a --- /dev/null +++ b/pages/en/configuration/configuration.md @@ -0,0 +1,656 @@ +--- +categories: + - configuration +top: 70 +--- + +# Configuration + +### Initial config + +::: tip +After modifying the configuration file, restart OpenList for changes to take effect + +- Windows/macOS: `/data/config.json` +- Linux: one-click script directory, `/opt/openlist/data/config.json` or `/data/config.json` +- Docker: `/data/config.json` +- OpenWrt: modify config on server if using `luci-app-openlist` , otherwise `/data/config.json` +- Other: `/data/config.json` + +::: + +```json +{ + "force": false, + "site_url": "", + "cdn": "", + "jwt_secret": "random_generated", + "token_expires_in": 48, + "database": { + "type": "sqlite3", + "host": "", + "port": 0, + "user": "", + "password": "", + "name": "", + "db_file": "data\\data.db", + "table_prefix": "x_", + "ssl_mode": "", + "dsn": "" + }, + "meilisearch": { + "host": "http://localhost:7700", + "api_key": "", + "index": "openlist" + }, + "scheme": { + "address": "0.0.0.0", + "http_port": 5244, + "https_port": -1, + "force_https": false, + "cert_file": "", + "key_file": "", + "unix_file": "", + "unix_file_perm": "", + "enable_h2c": false + }, + "temp_dir": "data\\temp", + "bleve_dir": "data\\bleve", + "dist_dir": "", + "log": { + "enable": true, + "name": "data\\log\\log.log", + "max_size": 50, + "max_backups": 30, + "max_age": 28, + "compress": false, + "filter": { + "enable": false, + "filters": [ + { + "cidr": "", + "path": "/ping", + "method": "" + }, + { + "cidr": "", + "path": "", + "method": "HEAD" + }, + { + "cidr": "", + "path": "/dav/", + "method": "PROPFIND" + } + ] + } + }, + "delayed_start": 0, + "max_connections": 0, + "max_concurrency": 64, + "tls_insecure_skip_verify": true, + "tasks": { + "download": { + "workers": 5, + "max_retry": 1, + "task_persistant": false + }, + "transfer": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "upload": { + "workers": 5, + "max_retry": 0, + "task_persistant": false + }, + "copy": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "decompress": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "decompress_upload": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "allow_retry_canceled": false + }, + "cors": { + "allow_origins": ["*"], + "allow_methods": ["*"], + "allow_headers": ["*"] + }, + "s3": { + "enable": false, + "port": 5246, + "ssl": false + }, + "ftp": { + "enable": false, + "listen": ":5221", + "find_pasv_port_attempts": 50, + "active_transfer_port_non_20": false, + "idle_timeout": 900, + "connection_timeout": 30, + "disable_active_mode": false, + "default_transfer_binary": false, + "enable_active_conn_ip_check": true, + "enable_pasv_conn_ip_check": true + }, + "sftp": { + "enable": false, + "listen": ":5222" + }, + "mcp": { + "enable": false + }, + "last_launched_version": "OpenList version", + "proxy_address": "" +} +``` + +## Field Explanation + +### force + +By default OpenList reads the configuration from environment variables, set this field to `true` to force OpenList to read config from the configuration file. + +### site_url + +The address of your OpenList server, such as `https://pan.example.com`. This address is essential for some features, and thus thry may not work properly if unset: + +- thumbnailing `LocalStorage` +- previewing site after setting web proxy +- displaying download address after setting web proxy +- reverse-proxying to site sub directories +- ... + +Do not include the slash \(`/`\) at the end of the address. For example: + +```diff ++ "site_url": "https://openlist.example.com", +- "site_url": "https://openlist.example.com/", +``` + +### cdn + +The address of the CDN. Included `$version` values will be dynamically replaced by the version of OpenList. Existing dist resources are hosted on both npm and GitHub, which can be found at: + +- +- + +Thus it is possible to use any npm or ~~GitHub~~ CDN path for this field. Do not include the slash `/` at the end of the address. For example: + +- `https://registry.npmmirror.com/@openlist-frontend/openlist-frontend/$version/files/dist` +- `https://unpkg.com/@openlist-frontend/openlist-frontend@$version/dist` +- `https://cdn.jsdelivr.net/npm/@openlist-frontend/openlist-frontend@$version/dist` +- `https://fastly.jsdelivr.net/npm/@openlist-frontend/openlist-frontend@$version/dist` +- `https://gcore.jsdelivr.net/npm/@openlist-frontend/openlist-frontend@$version/dist` +- `https://jsd.onmicrosoft.cn/npm/@openlist-frontend/openlist-frontend@$version/dist` + +- ~~`https://cdn.jsdelivr.net/gh/OpenListTeam/OpenList-Frontend@$version/dist`~~ +- ~~`https://jsd.onmicrosoft.cn/gh/OpenListTeam/OpenList-Frontend@$version/dist`~~ + +::: tip +If you are using the lite version, please add `/lite` to the end of the URL. For example: `https://cdn.jsdelivr.net/gh/OpenListTeam/OpenList-Frontend@$version/dist/lite` +::: + +Keep empty to use dist resources embedded in the program by default. + +#### CDN for Beta version + +Since the frontend uses Vite for building, the generated JS files use a hash naming strategy. When code changes occur, the built file names change, making `index.html` files from different versions incompatible with each other. + +The OpenList backend needs to load `index.html` into memory for processing, including inserting code and modifying variables. If deployed only through Pages with modified API addresses, it would result in missing functionality and routing failures. + +To solve this problem, we've added the ability to fetch `index.html` from CDN for Beta versions and self-built versions, ensuring that JS files and `index.html` are properly matched. + +- **Release version**: Static resources are loaded through NPM CDN with fixed versions, so there's no need to request from CDN - the built-in `index.html` can be used directly + - Note: Some NPM CDNs (like npmmirror) may prohibit access to HTML files, but Release versions don't depend on CDN's `index.html`, so they're unaffected +- **Beta version**: Updates frequently, OpenListTeam doesn't upload to NPM, and no CDN is provided for Beta versions + +Since Beta versions don't have NPM CDN provided by OpenListTeam, you need to deploy it yourself: + +1. **Deploy frontend build artifacts** + - Deploy build artifacts to a CDN platform (you can use Cloudflare Pages, EdgeOne Pages, etc.) + - Configure necessary CORS headers, for example of `edgeone.json`: + + ```json + { + "headers": [ + { + "source": "/*", + "headers": [ + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "GET, OPTIONS" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Content-Type" + } + ] + }, + { + "source": "/**/*.mjs", + "headers": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ] + } + ] + } + ``` + + - Recommend using "copy + overwrite" deployment method, retaining old version resource files to ensure compatibility for versions not rebooted + +2. **Configure backend** + - Add CDN configuration in `config.json` + - The program will automatically fetch the latest `index.html` from CDN when starting + +3. **Version updates** + - When you need to update the frontend, simply restart the backend program + +Here's a diagram for better understanding: + +```mermaid +flowchart TD + A[Program Initialization] --> B{CDN Configured?} + + B -->|Yes| D{Version Type?} + B -->|No| E{Local dist_dir Configured?} + + E -->|No| F[Use Built-in index.html] + E -->|Yes| G[Use index.html from Local dist_dir] + + D -->|Beta Version| H[Fetch index.html from CDN] + D -->|Release Version| F + + H --> I{CDN Fetch Successful?} + I -->|Success| J[Use CDN-fetched index.html] + I -->|Failed| K[Resty Retry 3 Times] + K -->|Success| J + K -->|Still Failed| L[Program Exit, Wait for Restart] + + F --> M[Replace CDN, Site Info and Other Variables] + G --> M + J --> M + M --> N[Return Page] +``` + +### jwt_secret + +The secret used to sign the JWT token, randomly generated on first run. + +### token_expires_in + +User login expiration time, in hours. + +### database + +The database configuration, which is by default `sqlite3`. Available options are `sqlite3`, `mysql` and `postgres`. + +- The database options do not need to be modified if using `sqlite3`. + +```json + "database": { + "type": "sqlite3", //database type + "host": "", //database host + "port": 0, //database port + "user": "", //database account + "password": "", //database password + "name": "", //database name + "db_file": "data\\data.db", //Database location, used by sqlite3 + "table_prefix": "x_", //database table name prefix + "ssl_mode": "", //To control the encryption options during the SSL handshake, the parameters can be searched by themselves, or check the answer from ChatGPT below + "dsn": "" // https://github.com/alist-org/alist/pull/6031 + }, +``` + +::: details Expand to view details of `ssl_mode` +If you don't know how to fill it in, then your server probably doesn't have SSL enabled, so just leave it blank. +In MySQL, the `ssl_mode` parameter is used to specify the authentication mode of the SSL connection. Here are a few common options: + +- `DISABLED`: Disable SSL connections. +- `PREFERRED`: Use an SSL connection if server has SSL enabled, and otherwise fallback to a normal connection. +- `REQUIRED`: Force to use SSL connection and fail if the server does not support SSL connection. +- `VERIFY_CA`: Force to use SSL connection and verify the authenticity of the server certificate. +- `VERIFY_IDENTITY`: Force to use an SSL connection and verify the authenticity of the server certificate and that the name matches the connecting hostname. + Additional, MySQL 5.x and 8.x have differences. If you are using databases provided by service providers, BTFM. If you deployed the database yourself, STFW. + In PostgreSQL, the `ssl_mode` parameter is used to specify how the client uses SSL connections. Here are a few common options: +- `disable`: Disable SSL connections. +- `allow`: Allow SSL connections. +- `prefer`: Use an SSL connection if server has SSL enabled, and otherwise fallback to a normal connection. +- `require`: Force to use SSL connection and fail if the server does not support SSL connection. +- `verify-ca`: Force to use SSL connection and verify the authenticity of the server certificate. +- `verify-full`: Force to use an SSL connection and verify the authenticity of the server certificate and that the name matches the connecting hostname. + +::: + +::: details Notes on modifying the database when there is already data + +1. If you change the `sqlite` database to `mysql` database, it is first recommended to use the backup and recovery method. +2. If you directly import `sqlite` data into `mysql`, you can view this video tutorial: [View tutorial](https://www.bilibili.com/video/BV1iV4y1T7kh) + - Because when directly importing the cloud disk database table, the time of `sqlite` and the time of `mysql` are filled in differently, an error will be reported [please check the precautions and how to solve it](https://www.bilibili.com/video/BV1iV4y1T7kh?t=343.7) + +::: + +### meilisearch + +```json + "meilisearch": { + "host": "http://localhost:7700", // meilisearch host, the default is the local machine + "api_key": "", // if meilisearch's authentication is enabled, this is required + "index": "" // meilisearch index uid + }, +``` + +- Documentation link: +- Reference Links: + +### scheme + +The configuration of scheme. Set this field if using HTTPS. + +- Remember to copy the certificate file to the data directory. Config example: + +```json + "scheme": { + "address": "0.0.0.0", // The http/https address to listen on, default `0.0.0.0` + "http_port": 5244, // The http port to listen on, default `5244`, if you want to disable http, set it to `-1` + "https_port": -1, // The https port to listen on, default `-1`, if you want to enable https, set it to non `-1` + "force_https": false, // Whether the HTTPS protocol is forcibly, if it is set to True, the user can only access the website through HTTPS + "cert_file": "data\\cert.crt", // Path of cert file + "key_file": "data\\key.key", // Path of key file + "unix_file": "", // Unix socket file path to listen on, default empty, if you want to use unix socket, set it to non empty + "unix_file_perm": "", // Unix socket file permission, set to the appropriate permissions + "enable_h2c": false // Support HTTP/2 Cleartext (H2C) protocol for openlist's http service. The cleartext HTTP/2 protocol supports nginx's grpc_pass after it is enabled - https://github.com/AlistGo/alist/pull/8294 + }, +``` + +### temp_dir + +The directory to keep temporary files. By default OpenList uses `data/temp`. + +::: danger +temp_dir is a temporary folder exclusive to alist. In order to prevent OpenList from generating garbage files when being interrupted, the directory will be cleared every time OpenList starts, so do not store anything in this directory or map this directory & subdirectories to directories in use when using Docker. +::: + +### bleve_dir + +Where data is stored when using **`bleve`** index. + +### dist_dir + +If this option is set, the front-end files in the defined **local** external folder will be used as a priority. + +- Supports using front-end files from an external folder +- Supports using other front-end files, while the back-end continues to use the original version of the application + +Upload the front-end files (dist) to the application's `data` folder, then fill in: + +```json + "dist_dir": "data/dist", +``` + +### log + +The log configuration. Set this field to save detailed logs of disable. + +```json + "log": { + "enable": true, // Whether OpenList should store logs + "name": "data\\log\\log.log", // The path and name of the log file + "max_size": 10, // the maximum size of a single log file, in MB. After reaching the specified size, the file will be automatically split. + "max_backups": 5, // the number of log backups to keep. Old backups will be deleted automatically when the limit is exceeded. + "max_age": 28, // The maximum number of days preserved in the log file, the log file that exceeds the number of days will be deleted + "compress": false, // Whether to enable log file compression functions. After compression, the file size can be reduced, but you need to decompress when viewing, and the default is to close the state false + "filter": { // skip some logs output, not enable by default + "enable": false, + "filters": [ // preset example + { + "cidr": "", + "path": "/ping", // Health check + "method": "" + }, + { + "cidr": "", + "path": "", + "method": "HEAD" // HEAD request + }, + { // WebDav metadata + "cidr": "", + "path": "/dav/", + "method": "PROPFIND" + } + ] + }, +``` + +Each filter acts as the following object: + +```json +{ + "cidr": "", + "path": "", // http path, If it starts with "/", it is an absolute path; if it does not start with "/", it is a relative path + "method": "" // HTTP/webdav method, in uppercase +} +``` + +Take note of the startup log to confirm the load, as detailed in the source code `server/middlewares/filtered_logger.go`. + +### delayed_start + +Whether to delay OpenList startup. + + + +**Time unit: second** + +Generally this option is used when OpenList is configured to auto-start. The reason is that sometimes network takes some time to connect, so drivers requiring cannot start correctly after OpenList starts. + +### max_connections + +The maximum amount of connections at the same time. The default is 0, which is unlimited. + +- 10 or 20 is recommended for general devices such as N1(S905D). +- Usage Scenarios: the device will crash if the device is bad at concurrency when picture mode is enabled. + +### max_concurrency + +Limit the maximum concurrency of local agents. The default value is 64, and 0 means no limit. + +### tls_insecure_skip_verify + +Whether not to verify the SSL certificate. + +If there is a problem with the certificate of the website used when this option is not enabled (such as not including the intermediate certificate, having the certificate expired, or forging the certificate, etc.), the service will not be available. + +When this option is enabled, please ensure the program is running in a safe network environment. + +### tasks + +Configuration for background task threads. + +```json + "tasks": { + "download": { + "workers": 5, + "max_retry": 1, + "task_persistant": false + }, + "transfer": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "upload": { + "workers": 5, + "max_retry": 0, + "task_persistant": false + }, + "copy": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "decompress": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "decompress_upload": { + "workers": 5, + "max_retry": 2, + "task_persistant": false + }, + "allow_retry_canceled": false + }, +``` + +- **workers**: Number of task threads. +- **max_retry**: Number of retries. + - 0: Retries disabled. +- **download**: Download task when downloading offline +- **transfer**: upload transfer task after offline download is completed +- **upload**: upload task +- **copy**: copy the task +- **decompress**:decompress the task +- **decompress_upload**:decompress upload the task +- **task_persistant**:The task is persistent and will not be cancelled after restarting `OpenList` + - **download**:false + - **transfer**:false + - **upload**:false + - **copy**:false + - **decompress**:false + - **decompress_upload**:false +- **allow_retry_canceled**:Allow users to retry previously canceled tasks + +--- + +A new **transmission** configuration path is added to the background configuration: `/@manage/settings/traffic` + +- Supports limiting the number of threads and transmission uplink and downlink rates of **6 tasks** +- **** + Operation principle: If `settings/traffic` does not have a thread number field (first run or just upgraded from an old version), `settings/traffic` will be initialized with the value of the config configuration file. If `settings/traffic` has a value, the thread configuration information of config will be ignored +- **** +- Summary: For newly installed or upgraded versions, the values will be read from the configuration file to initialize the `traffic` configuration information. Subsequent modifications to the thread only need to be modified in the background. + +### cors + +Configuration for Cross-Origin Resource Sharing (CORS). + +```json + "cors": { + "allow_origins": [ + "*" + ], + "allow_methods": [ + "*" + ], + "allow_headers": [ + "*" + ] + } +``` + +- **allow_origins**: Allowed sources. +- **allow_methods**: Allowed request methods. +- **allow_headers**: Allowed request headers. + +Use it to understand it by yourself, and then configure it. If you do n’t know, please do n’t modify it at will. Use the default configuration. + +### S3 + +```json + "s3": { + "enable": false, + "port": 5246, + "ssl": false + } +``` + +- `enable`:Whether the S3 function is enabled, the default is not enabled +- `port`:port +- `SSL`:Enable the HTTPS certificate, not enabled by default + +Function introduction: [Click to view](../guide/advanced/s3.md) + +### ftp + +```json + "ftp": { + "enable": false, + "listen": ":5221", + "find_pasv_port_attempts": 50, + "active_transfer_port_non_20": false, + "idle_timeout": 900, + "connection_timeout": 30, + "disable_active_mode": false, + "default_transfer_binary": false, + "enable_active_conn_ip_check": true, + "enable_pasv_conn_ip_check": true + }, +``` + +- `enable`: Whether the **ftp** function is enabled, not enabled by default +- `listen`: port number +- `find_pasv_port_attempts`: maximum number of attempts to re-find a port due to port conflicts during passive transmission +- `active_transfer_port_non_20`: enable ports other than 20 as active transmission ports +- `idle_timeout`: maximum idle time (seconds) when there is no client request +- `connection_timeout`: connection timeout +- `disable_active_mode`: disable active transmission mode +- `default_transfer_binary`: transfer in binary mode by default +- `enable_active_conn_ip_check`: perform IP check on the client side of the TCP connection of the data stream in active transmission mode +- `enable_pasv_conn_ip_check`: perform IP check on the client side of the TCP connection of the data stream in passive transmission mode + +Other instructions: [Click to view](../guide/advanced/ftp.md) + +### sftp + +```json + "sftp": { + "enable": false, + "listen": ":5222" + } +``` + +- `enable`: Whether the **sftp** function is enabled, not enabled by default +- `listen`: port number + +Other instructions: [Click to view](../guide/advanced/ftp.md) + +### mcp + +```json + "mcp": { + "enable": false + } +``` + +- `enable`: Whether the **MCP** endpoint is enabled, not enabled by default + +Other instructions: [Click to view](../guide/advanced/mcp.md) + +### proxy + +```json + "proxy_address": "", +``` + +Supports HTTP proxy, HTTPS proxy, SOCKS4 proxy, SOCKS5 proxy, SOCKS5HOSTNAME proxy. + +Both IPv4 and IPv6 are supported. diff --git a/pages/en/configuration/global.md b/pages/en/configuration/global.md new file mode 100644 index 000000000..9ad871181 --- /dev/null +++ b/pages/en/configuration/global.md @@ -0,0 +1,177 @@ +--- +categories: + - configuration +top: 20 +--- + +# Global Configuration + +## Hide files + +Match files hidden by a regular expression (`JavaScript`). Please do not fill it in casually, as incorrect expressions may cause the front-end page to crash. + +One regular expression per line. + +By default, there is an expression `/\/README.md/i`, which hides `README.md` from all directories. + +Note that this is not a true hide. The file will still appear in the API response list, but will not be displayed in the front-end interface. If you wish to truly hide the file, please refer to [metadata](../guide/advanced/meta.md). + +## Package download + +Whether to enable package download (default is true). **Not recommended, especially for large or numerous files.** + +- It is recommended to push files to Aria2 for downloading, as it supports maintaining the directory structure when saving the download folder. +- For more details, see [What is the difference between the two Aria2](./other.md#other). + +## Customize head + +The content set here will be automatically inserted into the header of the webpage (not include the manage pages). You can reference scripts, CSS, etc., here to style the frontend of OpenList. + +- How to configure PWA(Desktop、Android、IOS):**[alist/issues/6724](https://github.com/alist-org/alist/issues/6724#issuecomment-2220251541)** + +## Customize body + +The content set here will be automatically inserted at the end of the webpage body. You can add icp information, visit statistics, etc., here. + +## Link expiration + +The expiration time of the direct link, in hours. If it equals 0, it will not expire. Default is 0. + +::: warning +Only the straight chain of the path with the password added will have an expiration time, otherwise it will not expire.Because the expiration time is added to the sign query parameter, and the path without adding the password will not check the sign. +::: + +## Sign all + +Add signatures to the direct link of all files (whether with password or not), such as `https://xxxx.com/d/xx?sign=vUQ5KFXnwMseKnIUXGRcfoG3cEHzKFBiPGp1NriMDXA=:0`. + +If you need to close it, you can close it yourself, but you need to pay attention to security issues. After closing the signature, if the site can be accessed by the public network, the password may be bypassed to access private files. +There are two other methods that also carry the `sign?xxx` parameter: + +1. Add Storage Select `Enable Signing` +2. Meta Information Add Password + +The scope of the three methods: `Sign All` > `Meta Information Add Password` > Add Storage Select `Enable Signing` + +1. Sign All: If this option is turned on, the sign parameter will be carried regardless of whether meta-information is encrypted or not, and whether `Enable Signing` is checked when adding storage. +2. Meta Information Add Password:Only files under this meta information path will carry the sign parameter. + - If **Apply to sub folder** is turned on, all files in this path will carry the sign parameter +3. Add Storage Select `Enable Signing`:Only this storage driver carries the sign parameter. + +## Privacy regs + +What you don't want to show in the error message, One regular expression (in `Golang`) per line. The matched content will be replaced with `*` of the corresponding length. + +## Ocr api + +Used to identify verification codes. You can deploy yourself: https://hub.docker.com/r/cloudlinksu/openlist-ocr-server. + +The default ocr api is deployed on the [Hugging Face](https://huggingface.co/spaces/Susus21/openlist-ocr/tree/main). You can clone the Hugging Face repository to build your own: [clone the Hugging Face repository](https://huggingface.co/spaces/Susus21/openlist-ocr/tree/main?duplicate=true). + +The hf domain name after successful self-construction is `https://{username}-{repositroy-name}.hf.space/ocr/file/json`. + +## Filename char mapping + +Map certain special characters, such as `/`, which is used as a path separator in OpenList. When file names contain `/`, it may cause issues like broken file paths or inability to view the files. By using this method, we can map and convert these characters to resolve such problems. + +```json +{ "/": "|", "xx1": "xx1", "xx2": "xx2" } +``` + +## Forward direct link params + +After enabling, the parameters after `?` will be automatically appended to the end of the direct link URL. + +## Ignore direct link params + +Ignore the parameters for forwarding direct links, such as `sign` and `openlist_ts`. + +## Webauthn login enabled + +**Web Authentication (WebAuthn)** is a new authentication method. You can enable it by following these steps: + +1. **Enable WebAuthn Feature**: Go to the admin panel, navigate to `Settings` → `Global`, and enable the option `Enable WebAuthn Login`. +2. **Bind WebAuthn Credentials**: Go to the `Profile` page in the admin panel and click `Add WebAuthn Credential` to bind your credentials. + + Supported authentication methods: + - Local PIN code + - Companion devices (e.g., smart bands, smartwatches) + - Windows Hello options (e.g., facial recognition, fingerprint recognition) + +3. **Log in with WebAuthn**: After binding, you can log in using WebAuthn: + - On the login page, click the login button on the far right. + - Enter your username. + - Click login. + - Follow the prompts to complete WebAuthn verification (unlock the respective authentication method). + +::: tip WebAuthn only supports secure origins. + +**Supported environments:** + +- Websites with `https://` protocol +- Localhost environment (`localhost`) + +**Unsupported environments:** + +- `http://` protocol +- Local network IPs (e.g., `192.168.x.x`) +- Local IPs (e.g., `127.0.0.1`) +- Direct access via server IP + +::: + +## Allow previewing sharing files + +Enable the preview feature for files in share links. + +Please note that disabling this option will only hide preview methods (except Download) on the frontend, but cannot prevent users from invoking the relevant APIs. + +## Allow previewing sharing archives + +Enable the preview feature for archives in share links. + +Unlike the previous setting, disabling this option will prevent calls to the archive preview APIs for files in share links. + +## Force proxy sharing files + +Enforce proxying for all file requests originating from share links. + +## Share summary content + +The content copied by clicking "Copy Link" after sharing completion, coding in Handlebars template syntax. + +If you prefer copying the accessible link directly, try: + +- Preview link + +```handlebars +{{base_url}}/@s/{{id}} +``` + +- Preview link with share code + +```handlebars +{{base_url}}/@s/{{id}}{{#if pwd}}?pwd={{pwd}}{{/if}} +``` + +- direct download link (applies only to single-file shares) + +```handlebars +{{base_url}}/sd/{{id}}{{#if pwd}}?pwd={{pwd}}{{/if}} +``` + +## Handle hook after writing + +Whether to trigger the directory update hook after operations such as upload, rename, delete, move, copy, or extraction. + +Triggering the directory update hook will cause the index to update and activate the function of the [Strm](/en/guide/drivers/strm) driver to generate local files. + +## Handle hook rate limit + +It is only meaningful when the [Handle hook after writing](/en/configuration/global#handle-hook-after-writing) is enabled. When the directory update hook is triggered, this limits the rate at which the driver API is called (unit: times/second; 0 means no restriction). + +## Ignore system files + +When enabled, attempts by users to upload certain system files will directly fail, thereby achieving filtering. + +The determination of whether a file is a system file is based on its filename. diff --git a/pages/en/configuration/index.md b/pages/en/configuration/index.md new file mode 100644 index 000000000..12e064860 --- /dev/null +++ b/pages/en/configuration/index.md @@ -0,0 +1,11 @@ +# Configuration + +--- + +- [Configuration file](./configuration) +- [Site settings](./site) +- [Style settings](./style) +- [Preview settings](./preview) +- [Global settings](./global) +- [Other settings](./other) +- [Side settings](./side) diff --git a/pages/en/configuration/other.md b/pages/en/configuration/other.md new file mode 100644 index 000000000..966c1fbc3 --- /dev/null +++ b/pages/en/configuration/other.md @@ -0,0 +1,53 @@ +--- +categories: + - configuration +top: 10 +--- + +# Other Configuration + +## Aria2 + +Used for the offline download feature, which downloads an external resource outside of OpenList into the storage managed by OpenList. +Set Aria2 uri and Aria2 for offline download. + +> `Aria2` needs to access the same directory as `OpenList`: `/opt/openlist/data/temp/aria2`. Assuming that both Aria2 and OpenList are independent containers, where OpenList mounts the directory `/etc/openlist/data:/opt/openlist/data`, Aria2 containers need to be additionally mounted under the path `/etc/openlist/data/temp/aria2:/opt/openlist/data/temp/aria2`. + +### Aria2 uri + +The Aria2 RPC address used for offline downloading. The default value is: `http://localhost:6800/jsonrpc`. + +### Aria2 secret + +The Aria2 RPC secret key used for offline downloading. The default value is empty. + +## qBittorrent + +Used to customize **qBittorrent** parameters to configure the client to use. + +The default value is: `http://admin:adminadmin@localhost:8080/`, you can modify it by referring to [specific instructions](../guide/advanced/offline-download.md#qbittorrent) + +## 115、PikPak、Thunder + +**You need to add the driver first, and then set the temporary directory in the settings.** + +Allow the use of offline download tools such as 115/PikPak/Thunder in any storage. + +- Files will be downloaded directly to the destination dir if using the tool in 115/PikPak/Thunder storage. +- Otherwise, files will be downloaded to a user-configured temp dir, and then transfered to the destination dir. + - For example, on the front-end page of the `GoogleDrive` storage drive, when the `Pikpak offline-download` function is invoked, the file will first be downloaded to the Pikpak temporary folder directory set in the backend. Once the Pikpak offline download is complete, the file will be automatically transferred from Pikpak to `GoogleDrive`. + +### Token + +The token that can be used to access all APIs of the program. Unlike the token obtained after logging in with a username and password, this token is fixed and has no expiration time. + +### Other + +1. When using OpenList, you may notice two Aria2 options. What is the difference between them? [**Click here for detailed explanation**](../faq/why.md#what-is-the-difference-between-the-two-aria2) + +2. Supports using Aria2 to download folders while preserving the original directory structure. + - **Configure Aria2**: Go to the bottom-right corner, click `Settings` → `Aria2 RPC Link` → Enter the `Aria2 RPC Key` (if available). + - Aria2 will download files locally, so you only need to initiate the download on your local machine. It also supports pushing the download task to another computer, your own server, or other devices in your local network, as long as the target device has Aria2 installed and is connected to either the public internet or the local network. + + - **Start downloading**: Check the `Enable checkbox` in the bottom-right corner → Select the files/folders → Click `Download` at the bottom → `Send to Aria2`. + - **Important notes**: It is recommended not to download too many files at once, such as thousands of folders or tens of thousands of files, as this may cause performance issues. diff --git a/pages/en/configuration/preview.md b/pages/en/configuration/preview.md new file mode 100644 index 000000000..90417066e --- /dev/null +++ b/pages/en/configuration/preview.md @@ -0,0 +1,160 @@ +--- +categories: + - configuration +top: 30 +--- + +# Preview Configuration + +## Text types + +The extensions of the files you want to preview as text, split by `,`, such as `txt,md,go,tsx`. + +## Audio types + +The extensions of the files you want to preview as audio, split by `,`, such as `mp3,wav,m4a`. + +## Video types + +The extensions of the files you want to preview as video, split by `,`, such as `mp4,webm,ogg`. + +## Image types + +The extensions of the files you want to preview as image, split by `,`, such as `jpg,jpeg,png,gif,webp`. + +### Proxy types + +The file extensions to be downloaded through the program proxy, split by `,`, such as `m3u8,url`. + +### Proxy ignore headers + +Do not forward request headers, such as `authorization,referer`, when downloading through the program proxy. + +This can prevent download failures caused by header parameter leakage. + +### External previews + +A json object that contains the external preview settings, It is defined as + +```typescript +interface { + [extensions: string]: { + [name: string]: string //(url) + } +} +``` + +the first key `extensions` is the file extensions separated by `,` (if it starts with `/`, it will be considered a regular expression), and the value is also a `key-value` object, the key is the preview name, and the value is the external url. + +For the external URL, we provide some variables for you to use: + +Basic variable: + +- `$url`: the URL of file, such as `https://openlist.example.com/p/file.pdf`. +- `$durl`: the direct URL of file, such as `https://oss.example.com/cloud/user/2020/01/01/file.pdf`. +- `$name`: the file name + +Extended variables: +Add [eb_] before the basic variable, where e means `encodeURIComponent`, b means `base64`, such as: + +- `$e_url`: encodeURIComponent($url) +- `$b_url`: btoa($url) +- `$eb_url`: encodeURIComponent(btoa($url)) + +Finally, the `External previews` will displayed a `Open with` menu while current file matched the `extensions`. +For example, set `External previews` to + +```json +{ + "txt": { + "Notepad": "notepad://$url" + }, + "/.*/": { + "VSCode": "vscode://$url" + } +} +``` + +when we enter a file with the extension `txt`, it will show: +![Open-with](/img/config/open-with.png) + +### Iframe previews + +Similar to `External previews`, but it will embed an iframe in current page directly. + +You need to enter a preview HTML page, and then pass the file address as a parameter to the page using a variable. The available variables are listed in the previous section. + +:::tip +If you want to use self-deployed OnlyOffice to preview office files, you can add the configuration like this here: + +```json +{ + "doc,docx,xls,xlsx,ppt,pptx": { + "OnlyOffice": "you_only_office_url" + } +} +``` + +Deploying and using `OnlyOffice` Reference: + +- https://github.com/AlistGo/alist/discussions/3255 +- https://github.com/AlistGo/alist/discussions/8271 + +::: + +### Audio cover + +The default audio cover. + +### Audio autoplay + +Whether to automatically play audio files. + +### Video autoplay + +Whether to automatically play video files. + +### Preview archives by default + +After enabling this option, compressed files will be previewed by default (as shown in the image below). Although it will consume some server bandwidth, a full download will not occur. If you wish to disable the preview for compressed files, turning off this option will change the default behavior to download mode. + +![](/img/advanced/user_read_archives_light.png#light) + +![](/img/advanced/user_read_archives_dark.png#dark) + +### Readme autorender + +After closing, the content of files like readme.md will no longer be automatically rendered. + +By default, `readme.md`, `footer.md`, and `bottom.md` will be rendered at the bottom of the file, while `header.md`, `top.md`, and `index.md` will be rendered at the top of the file. + +### Filter readme scripts + +Prevent virus script attacks. After turning it on, the contents of `readme.md` will be displayed in text form. + +- Including but not limited to strikethrough, tables, task lists, etc. displayed in text form +- The specific effects are as follows: + ![](/img/config/readme_b.png#light) + +### Force preview + +We can force the preview type through the `type` query parameter. + +Available values: + +``` +UNKNOWN +FOLDER +VIDEO +AUDIO +TEXT +IMAGE +``` + +Example: `http://yourdomain/test-file.ahk?type=text` + +## Specify preview + +We can specify the preview through the `preview` query parameter. The parameter will be generated when you select a preview in the file page. + +Example: `http://yourdomain/test-file.ahk?preview=download` diff --git a/pages/en/configuration/side.md b/pages/en/configuration/side.md new file mode 100644 index 000000000..1f296c30c --- /dev/null +++ b/pages/en/configuration/side.md @@ -0,0 +1,136 @@ +--- +categories: + - configuration +top: 0 +--- + +# Side Configuration + +We can see a icon on the lower right corner of the homepage. If your computer resolution is very high, the icon is likely to be smaller +After the expand + +- + Refresh + + The refresh we mentioned is not to use F5 to refresh the webpage, **but use this icon to refresh**, so that the files that just uploaded to the network disk can be refreshed out. + +- + New File + +- + New Folder + +- + Recursive Move + + Move all the files in the current directory and all files in the subfolder to the designated folder + - :warning: Dangerous operation, please use it with caution. + +- + Remove Empty Folder + +- + Batch Rename + +- + Upload + + Open the upload modal. + +- + Offline download + + Open the offline download modal. + +- + Toggle Outline + + Toggle the outline for markdown viewer. + +- + Toggle Checkbox + + Select multiple files. + - In the display of Toggle Checkbox, first trigger the selection operation to prevent jumping into a deeper folder + - In the display of Toggle Checkbox, change it to hold down the `Alt` key and click to open the folder (macOS Press and hold the `Option` key) + +- + Local Settings + + Open the local settings. + +::: tip +If you didn't find this feature, please update the version +::: + +## Upload Modal + +![](/img/config/side/upload-modal-en.png) + +Drag files to the field to upload, or click `Choose Folder` or `Choose File` to select files to upload. + +There are two methods to upload files: + +1. **Stream**: directly puts the binary content of the file into the body, so that the backend can use very little memory, it seem to have no downsides. + +2. **Form**: is to wrap the file into formdata and upload it. The memory used by the backend will be more, but there is no limit on the size. + +There are the following options: + +- **Add as Task**: Create an upload task in the background. +- **Overwrite existing File**: If the file already exists, it will be overwritten. +- **Try rapid**: Upload the file with its checksum information, requires cloud storage support. + +## Offline Download Modal + +![](/img/config/side/offline-download-modal-en.png) + +Download the file offline and upload it to the network disk directory you designated. + +If you want to keep the offline downloaded file, please add the link first, select the options below, and then start the offline download. + +**How to use**: Navigate to the target directory, then select "Offline Download" from the bottom-right corner. + +Supported offline download tools: `Aria2`, `qBittorrent`, `SimpleHttp`. + +**Note**: When using the offline download feature, OpenList must be installed and running on the corresponding device. Offline downloads cannot be performed across different devices. + +For more information, please refer to [Offline Download](../guide/advanced/offline-download.md). + +## Local Settings Modal + +Local settings will be saved to the browser's local storage. + +1. **Aria2 RPC URL** and **Aria2 RPC secret**: This field is for entering the user's Aria2 information, which enables batch downloading on the client side, not on the server side. + +2. **Global default layout**: The selection of view selection in the upper right corner is to enter that folder to modify it only for the only folder. This setting can consistently take effect without using a folder setting. + +3. **Show folder in image view**: literal meaning + +4. **Show sidebar**: Add a list directory on the left side of the list like Windows Explorer, which can jump to a deep folder faster + +5. **Show file count message**: At the bottom of the page (in list view) or at the top (in grid or image view), display the total number of items in the current folder and how many items are selected. + +6. **Position of header & nav bar**: Is the logo and navigation bar displaying at the drop -down list? + - **Normal**: No need to display + - **Stick to top of page**: adsorb LOGO and navigation bar + - **Only nav bar sticky**: Do not adsorb LOGO, only adsorb navigation bar + +7. **Grid item size** : Adjust the picture size in the Grid item mode. Default: 90. + +8. **List item filename overflow**: When the text is too long, the three state selection + - **Ellipsis**: Default + - **Scrollable**: PC users use the `Shift` + `mousewheel` on the file, if it is a mobile phone user sliding the file left and right + - **Multi-line** + +9. **Open item on Checkbox**: + - **Direct**: Open the file/enter the folder + - **Double Click** + - **Disable while checked** + - With `Ctrl` or `Command` (macOS) hold + - With `Alt` or `Option` (macOS) hold + - If the button option does not take effect, please check whether the browser plugin conflict + + Tips: You can press the `Shift` key to select one, and then select one end. You can quickly select all the files in the middle + +10. **Language package selection** and **night mode and daytime mode switch** diff --git a/pages/en/configuration/site.md b/pages/en/configuration/site.md new file mode 100644 index 000000000..465765c22 --- /dev/null +++ b/pages/en/configuration/site.md @@ -0,0 +1,73 @@ +--- +categories: + - configuration +top: 50 +--- + +# Site Configuration + +## Version + +The version of OpenList, readonly. + +## Site title + +The title of OpenList, such as `OpenList`. + +## Announcement + +The announcement of OpenList, such as `### Hello\nWelcome to use OpenList`. We suggest you add a title in front of the content, such as `### Hello`, so that the content will not be covered by `Close Button`. + +- If you don't want to display it, you can clear the contents of the announcement to not display it. +- If you think the `x` button in the upper right corner is in the way, you can use the following CSS to remove it + +```css + +``` + +## Pagination type + +- All: All files will be displayed at once. +- Pagination: Show a `Paginator` at the bottom of the page. +- Load more: Show a `Load more` button at the bottom of the page. +- Auto load more: Automatically load more files when scrolling to the bottom of the page. + +## Default page size + +The default page size of the `openlist` if `Pagination type` doesn't set to `All`, such as `20`. + +## Allow indexing + +Whether to allow others to mount your OpenList to build the index, check it and enable it. + +The default is off, use with caution. (New features in version 3.8.0) + +## Allow mounted + +Whether to allow being mounted by other OpenList instances. + +**The default is enabled. If you don't want to be mounted by others, it's recommended to disable this option.** (New feature in v3.16.3) + +## robots.txt + +Crawler configuration/rules. + +The default is to allow crawlers to access all pages. + +`Allow: /` Indicates that the crawlers of search engines are allowed to visit all pages: + +```txt{2} +User-agent: * +Allow: / +``` + +If you want to prevent crawlers from accessing all pages, you can change it to this: + +```txt{2} +User-agent: * +Disallow: / +``` diff --git a/pages/en/configuration/style.md b/pages/en/configuration/style.md new file mode 100644 index 000000000..412db331e --- /dev/null +++ b/pages/en/configuration/style.md @@ -0,0 +1,39 @@ +--- +categories: + - configuration +top: 40 +--- + +# Style Configuration + +## Logo + +The logo is displayed in the upper left corner of the page. The default is the `OpenList logo`. + +You can set two different ones at night and during the day, one link per line. + +## Favicon + +The favicon is displayed in the browser tab. The default is the `OpenList logo`. + +## Main color + +The main color is used to on the page. The default is `#1890ff`. + +## Home icon + +The home icon is displayed in the left of the navigation bar. The default is `🏠`. + +## Home container + +- Max 980px: the main width will set to `min(99%, 980px)` +- Hope container: sets the max-width of the content to match the min-width of the current breakpoint, refer to https://openlistteam.github.io/hope-ui/docs/layout/container + +## Settings layout + +The layout of the settings items on the management page. + +- Responsive: grids next to each other +- List style: one grid per row + +The default is: List diff --git a/pages/en/ecosystem/index.md b/pages/en/ecosystem/index.md new file mode 100644 index 000000000..bf4df5353 --- /dev/null +++ b/pages/en/ecosystem/index.md @@ -0,0 +1,214 @@ +--- +categories: + - ecosystem +top: 1000 +--- + +# OpenList Ecosystem + +This page shows the OpenList ecosystem, including official projects and third-party projects + +## Official Projects + +### [OpenList Docs ↗](./official_docs) + +Documentation based on Valaxy, supporting multiple styles and real-time builds from the GitHub repository. + +--- + +### [OpenList Desktop ↗](./official_desktop) + +A cross-platform GUI management interface integrating OpenList and rclone, offering graphical configuration, monitoring, and service management features. + +--- + +### [OpenList Mobile ↗](./official_mobile) + +A mobile file server application based on OpenList, built with Flutter framework. Turn your phone into a file server. + +--- + +### [OpenList APIPages ↗](./official_APIpage) + +An open-source implementation of the online API functionality in OpenList, deployable on various servers, providing stable and privacy-protected API authentication services for cloud storage users. + +--- + +### [OpenList Proxy ↗](./official_proxy) + +OpenList traffic proxy program, which can use another server to proxy OpenList traffic. + +### [OpenList Worker ↗](./official_worker/) + +The official TypeScript + Serverless port of OpenList running on Cloudflare Workers / EdgeOne / Alibaba Cloud ESA, reusing the official React 19 frontend. + +--- + +## Third-Party Open Source Projects + +::: tip +Third-party open source projects are based on community open-source security. The following projects are not affiliated with OpenListTeam. Please evaluate the associated risks before deploying them to a production environment. + +If you have a new project that is not listed here, you can submit it through [Issues](https://github.com/OpenListTeam/OpenList-Docs/issues) or [Pull Request](https://github.com/OpenListTeam/OpenList-Docs/pulls). +::: + +### [sbwml/luci-app-openlist ↗](https://github.com/sbwml/luci-app-openlist) + +Provides LuCI support for OpenList, suitable for OpenWRT-based router systems. + +Free and Open Source Software licensed under the MIT license. + +--- + +### [giturass/openlist_termux ↗](https://github.com/giturass/openlist_termux/) + +OpenList management script for Termux, used for convenient installation, updating, and management of OpenList in the Android Termux environment. + +Free and Open Source Software licensed under the MIT license. + +--- + +### [ypq123456789/openlist-manger ↗](https://github.com/ypq123456789/openlist-manger) + +A cross-platform interactive Shell script for managing OpenList, supporting Linux, macOS, Windows (WSL), and Android Termux. + +Free and Open Source Software licensed under the MIT license. + +--- + +### [Alien-Et/OpenList-Magisk ↗](https://github.com/Alien-Et/OpenList-Magisk) + +The OpenList Magisk module integrates the OpenList file server into the Android system, running in a systemized manner through Magisk, supporting ARM and ARM64 architectures. + +Free and Open Source Software licensed under the MIT license. + +--- + +### [qianye216/OpenList-Desktop ↗](https://github.com/qianye216/OpenList-Desktop) + +OpenList Desktop is a cross-platform desktop client designed for OpenList (Alist) and Rclone. It provides a modern and user-friendly graphical interface that helps you easily manage Alist services and Rclone cloud storage mounts without needing to memorize and input tedious command-line instructions. + +Free and Open Source Software licensed under the GPLv3 license. + +--- + +### [LeoHaoVIP/AListLiteAndroid ↗](https://github.com/LeoHaoVIP/AListLiteAndroid) + +AListLite is an Android application that supports running OpenList services. Compared to the tutorial described in BV1ex4y157xJ, users can use OpenList services on Android devices (phones/TVs) without needing to install Termux. + +Free and Open Source Software licensed under the AGPLv3 license. + +--- + +### [outlook84/mpv-handler-openlist ↗](https://github.com/outlook84/mpv-handler-openlist) + +This is a URL protocol handler (mpv://) designed for mpv or mpv.net media players on Windows platform. This tool is used to invoke mpv or mpv.net players from OpenList web pages to open video links. + +Free and Open Source Software licensed under the GPLv2 license. + +--- + +### [outlook84/openlist-tvbox-gateway ↗](https://github.com/outlook84/openlist-tvbox-gateway) + +OpenList gateway for TVBox, used to connect OpenList resources to TVBox-compatible clients. + +Free and Open Source Software licensed under the AGPLv3 license. + +--- + +### [moyanj/AList3SDK ↗](https://github.com/moyanj/AList3SDK) + +A high-performance Python SDK for easy interaction with OpenList/AList services. Supports both asynchronous and synchronous APIs, and provides comprehensive type annotations! + +Free and Open Source Software licensed under the LGPL-2.1 license. + +--- + +### [huancun/Openlist-EX-container ↗](https://github.com/huancun/Openlist-EX-container) + +This project is an improved version based on Alist-EX-container. +Integrates Openlist, Aria2, qBittorrent, Caddy reverse proxy, supports multi-arch one-click deployment, navigation page and multiple web panels, perfect for offline download and resource management. + +Free and Open Source Software licensed under the GPLv3 license. + +--- + +### [hienao/ostrm ↗](https://github.com/hienao/ostrm) + +A full-stack application for converting OpenList file lists into STRM streaming media files. (Originally the openlist-strm project) + +Free and Open Source Software licensed under the GPLv3 license. + +--- + +### [li-peifeng/iNoi ↗](https://github.com/li-peifeng/iNoi) + +An open-source fork of OpenList focusing on stability. + +Free and Open Source Software licensed under the AGPLv3 license. + +--- + +### [907739769/OpenList-strm-RuoYi ↗](https://github.com/907739769/OpenList-strm-RuoYi/) + +An improved version of OpenList Strm based on RuoYi framework. + +Free and Open Source Software licensed under the MIT license. + +--- + +### [Liki4/qnap-openlist-webdav ↗](https://github.com/Liki4/qnap-openlist-webdav/) + +QNAP OpenList | The third-party open-source QPKG version of OpenList. + +Free and Open Source Software licensed under the AGPLv3 license. + +--- + +### [HisAtri/openlistapi ↗](https://github.com/HisAtri/openlistapi/) + +An open-source Python client implementing the OpenList API. + +Free and Open Source Software licensed under the MIT license. + +### [MY QNAP's OpenList ↗](https://www.myqnap.org/product/openlist/) + +QNAP's MyQNAP repository distributes the OpenList application for a fee of $6. +We have contacted the QNAP France team and, based on the AGPL and CC licenses, requested them to provide the hash values of the application or take it down. +The communication progress is as follows: + +1. The application is provided by QNAP officially, not uploaded by users. +2. The charging behavior is not for profit, so the CC license does not apply. +3. According to the AGPL license, if the application has not been modified, hash values like MD5, SHA, etc. must be provided. Currently, QNAP states that the relevant work is in progress. +4. Based on the above situation, QNAP has temporarily taken down the application and will update the page. Once the hash values are displayed, the download and distribution of OpenList will resume. +5. As of now, QNAP has not actually violated the license. Our investigation initiated to prevent open-source supply chain risks has been completed. + +::: details History information (outdated,archived at 2026-01-22) +The third-party closed-source QPKG version of OpenList. + +[Archived on 2025-7-13](https://web.archive.org/web/20250713005135/https://www.myqnap.org/product/openlist/) + +Proprietary software, paid license. + +Note: The source code is not open, violating the AGPLv3 license and the OpenList logo's CC BY-NC-SA 4.0 license. +::: + +## Third-Party Proprietary Software + +::: danger +The following lists several other third-party projects derived from OpenList that do not fall into the above categories. + +Any actions of these software/projects (including references to OpenList in their names, functionality design, content, or promotional methods) do not represent the stance of the OpenList project. The functionality, consequences of use, and related legal responsibilities are entirely borne by their respective developers or publishers, and the OpenList project assumes no responsibility. +::: + +### [Yxiguan/OpenList_123](https://github.com/Yxiguan/OpenList_123) + +::: danger +The third-party closed-source modified version of OpenList. + +[Archived on 2025-10-19](https://web.archive.org/web/20251019150201/https://github.com/Yxiguan/OpenList_123) + +Proprietary software, no license. + +Note: The source code is not open, violating the AGPLv3 license. +::: diff --git a/pages/en/ecosystem/official_APIpage.md b/pages/en/ecosystem/official_APIpage.md new file mode 100644 index 000000000..b508bde48 --- /dev/null +++ b/pages/en/ecosystem/official_APIpage.md @@ -0,0 +1,288 @@ +--- +categories: + - ecosystem + - eco_official +top: 970 +--- + +# OpenList APIPages + +## What is OpenList APIPages + +### [OpenListTeam/OpenList-APIPages](https://github.com/OpenListTeam/OpenList-APIPages) + +[OpenListTeam/OpenList-APIPages](https://github.com/OpenListTeam/OpenList-APIPages) is a utility website led by [@PIKACHUIM](https://github.com/PIKACHUIM) and collaboratively developed with other [main contributors](https://github.com/OpenListTeam/OpenList-APIPages/graphs/contributors). The frontend is primarily used for initial authorization and obtaining refresh tokens of cloud storage clients, while the backend mainly supports a feature called "Online API", which enables remote token refresh functionality while protecting developer client secrets. + +## Why OpenList APIPages is needed + +Cloud Storage Background: + +- Most domestic cloud storage services do not provide official API access to individuals, or the application process is cumbersome, which is not conducive to rapid deployment. +- According to the cloud storage providers' management requirements, the obtained client keys must not be leaked. +- User authorization is in the form of refresh tokens, which need to be periodically combined with client keys to obtain refreshed access tokens from the cloud storage servers. +- API calls require the user's latest access tokens. + +Solution: + +- Community volunteers provide qualification certification and apply for API access permissions from cloud storage officials. +- Use relay servers to protect client keys. +- Periodically send refresh tokens to designated relay servers, which use built-in client keys to refresh access tokens. +- The relay server sends access tokens back to the OpenList client. + +The relay server described above is the backend functionality of the Online API server/APIPages, which works as follows: + +```mermaid +sequenceDiagram + participant OpenList + participant OnlineAPI as Online API Server + participant Server as Cloud Storage Server + + OpenList ->> OnlineAPI: Refresh Token + OnlineAPI ->> Server: Cloud Storage Client Key + Server -->> OnlineAPI: Access Token + OnlineAPI -->> OpenList: Access Token + OpenList ->> Server: Access Token + OpenList <<->> Server: Cloud Storage Data +``` + +The frontend part of APIPages also solves the following problems: + +- Initial authorization verification for different cloud storage services. +- Authorization while protecting built-in client keys. +- Authorization with custom client keys. +- Providing callback addresses for custom client keys. +- Other practical functions for cloud storage mounting. + +## How to use OpenList APIPages + + + +## When OpenList APIPages is not needed + +:::tip +For ordinary users, we strongly recommend using API servers provided by the community to reduce deployment difficulty. + +If you encounter failures in the network section, we recommend that you resolve. + +::: + +If you choose not to use community-provided servers or deploy your own server, please verify the following content: + +- 1. Have completely read the documentation related to the corresponding cloud storage driver. +- 2. Have completely read the open API development documentation provided by the corresponding cloud storage service. +- 3. Can read and understand the project code, have certain debugging capabilities, and can understand the corresponding logs and error messages. +- 4. Fully understand the rights granted to you by AGPLv3 and the parts we should be responsible for. +- 5. Can use basic GitHub functions and know how to **correctly** submit issues/pull requests to us. +- 6. Please remember that we have no way to solve your network problems. + +## APIPages Deployment Tutorial + +### One-Click Deployment + +- EdgeOne Functions International + + + Deploy with EdgeOne Pages + + +After deployment, please log in to the [EdgeOne Functions console](https://console.tencentcloud.com/edgeone/pages) to modify environment variables. Please refer to the [Variable Description](#variable-description) section. + +- EdgeOne Functions China + + + Deploy with EdgeOne Pages + + +After deployment, please log in to the [EdgeOne Functions console](https://console.cloud.tencent.com/edgeone/pages) to modify environment variables. Please refer to the [Variable Description](#variable-description) section. + +- Cloudflare Workers Global + + + Deploy to Cloudflare Workers + + +After deployment, please log in to the [Cloudflare Workers console](https://dash.cloudflare.com/) to modify environment variables. Please refer to the [Variable Description](#variable-description) section. + +### Container Deployment + +- Pull image + +``` +docker pull openlistteam/openlist_api_server +``` + +or + +``` +docker pull ghcr.io/openlistteam/openlist_api_server:latest +``` + +- Start project + +``` +docker run -d --name oplist-api-server \ + -p 3000:3000 \ + -e OPLIST_MAIN_URLS="api.example.com" \ + -e OPLIST_PROXY_API="gts.example.com" \ + -e OPLIST_ONEDRIVE_UID= `#optional` \ + -e OPLIST_ONEDRIVE_KEY= `#optional` \ + -e OPLIST_ALICLOUD_UID= `#optional` \ + -e OPLIST_ALICLOUD_KEY= `#optional` \ + -e OPLIST_BAIDUYUN_UID= `#optional` \ + -e OPLIST_BAIDUYUN_KEY= `#optional` \ + -e OPLIST_BAIDUYUN_EXT= `#optional` \ + -e OPLIST_CLOUD115_UID= `#optional` \ + -e OPLIST_CLOUD115_KEY= `#optional` \ + -e OPLIST_GOOGLEUI_UID= `#optional` \ + -e OPLIST_GOOGLEUI_KEY= `#optional` \ + -e OPLIST_YANDEXUI_UID= `#optional` \ + -e OPLIST_YANDEXUI_KEY= `#optional` \ + -e OPLIST_DROPBOXS_UID= `#optional` \ + -e OPLIST_DROPBOXS_KEY= `#optional` \ + -e OPLIST_QUARKPAN_UID= `#optional` \ + -e OPLIST_QUARKPAN_KEY= `#optional` \ + openlistteam/openlist_api_server:latest +``` + +- You can replace the image with ghcr: + + ``` + ghcr.io/openlistteam/openlist_api_server:latest + ``` + +- **Please make sure to modify your environment variables according to the environment variables below** + +- Environment Variable Description + +| Variable Name | Required | Variable Type | Variable Description | +| --------------------- | -------- | ------------- | ------------------------------------------------------------- | +| `OPLIST_MAIN_URLS` | Yes | string | Bind main domain, example: api.example.com | +| `OPLIST_PROXY_API` | No | string | Nodes deployed in mainland China need to specify Google proxy | +| `OPLIST_ONEDRIVE_UID` | No | string | OneDrive Client ID | +| `OPLIST_ONEDRIVE_KEY` | No | string | OneDrive Client Secret | +| `OPLIST_ALICLOUD_UID` | No | string | AliCloud Drive Developer AppID | +| `OPLIST_ALICLOUD_KEY` | No | string | AliCloud Drive Developer AppKey | +| `OPLIST_BAIDUYUN_UID` | No | string | Baidu NetDisk Application UID | +| `OPLIST_BAIDUYUN_KEY` | No | string | Baidu NetDisk Application Secret AppKey | +| `OPLIST_BAIDUYUN_EXT` | No | string | Baidu NetDisk Application SecretKey | +| `OPLIST_CLOUD115_UID` | No | string | 115 NetDisk Application ID | +| `OPLIST_CLOUD115_KEY` | No | string | 115 NetDisk Application Secret | +| `OPLIST_GOOGLEUI_UID` | No | string | Google Client ID | +| `OPLIST_GOOGLEUI_KEY` | No | string | Google Global API Key | +| `OPLIST_YANDEXUI_UID` | No | string | Yandex Application ID | +| `OPLIST_YANDEXUI_KEY` | No | string | Yandex Application Secret | +| `OPLIST_DROPBOXS_UID` | No | string | Dropbox Application ID | +| `OPLIST_DROPBOXS_KEY` | No | string | Dropbox Application Secret | +| `OPLIST_QUARKPAN_UID` | No | string | QuarkPan Application ID | +| `OPLIST_QUARKPAN_KEY` | No | string | QuarkPan Application Secret | + +### Edge Deployment + +- Clone code + +```shell +git clone https://github.com/OpenListTeam/OpenList-APIPages.git +``` + +- Modify configuration (CloudFlare only) + +Create and modify `wrangler.jsonc` + +```shell +cp wrangler.example.jsonc wrangler.encrypt.jsonc +``` + +Modify variable information: + +- MAIN_URLS: Domain name for deployment callback address +- Other parameters: Application information for each cloud storage service + +``` + "vars": { + "MAIN_URLS": "api.example.com", + "PROXY_API": "gts.example.com", + "onedrive_uid": "*****************************", + "onedrive_key": "*****************************", + "alicloud_uid": "*****************************", + "alicloud_key": "*****************************", + "baiduyun_uid": "*****************************", + "baiduyun_key": "*****************************", + "baiduyun_ext": "*****************************", + "cloud115_uid": "*****************************", + "cloud115_key": "*****************************", + "googleui_uid": "*****************************", + "googleui_key": "*****************************", + "yandexui_uid": "*****************************", + "yandexui_key": "*****************************", + "dropboxs_uid": "*****************************", + "dropboxs_key": "*****************************", + "quarkpan_uid": "*****************************", + "quarkpan_key": "*****************************" + }, +``` + +- Test code + +```shell +npm install + +# Run in Cloudflare Worker environment +npm run dev-cf + +# Run in Edgeone Functions environment +npm run dev-eo + +# Run in Node Service Work environment +npm run dev-js + +``` + +- Deploy project + +```shell +# Deploy in Cloudflare Worker environment +npm run deploy-cf + +# Deploy in Edgeone Functions environment +npm run deploy-eo + +# Run locally in Node Service Work +npm build-js && npm deploy-js +``` + +### Variable Description + +| Variable Name | Required | Variable Type | Variable Description | +| -------------- | -------- | ------------- | ------------------------------------------------------------- | +| `MAIN_URLS` | Yes | string | Bind main domain, example: api.example.com | +| `PROXY_API` | No | string | Nodes deployed in mainland China need to specify Google proxy | +| `onedrive_uid` | No | string | OneDrive Client ID | +| `onedrive_key` | No | string | OneDrive Client Secret | +| `alicloud_uid` | No | string | AliCloud Drive Developer AppID | +| `alicloud_key` | No | string | AliCloud Drive Developer AppKey | +| `baiduyun_uid` | No | string | Baidu NetDisk Application ID | +| `baiduyun_key` | No | string | Baidu NetDisk Application Secret AppKey | +| `baiduyun_ext` | No | string | Baidu NetDisk Application SecretKey | +| `cloud115_uid` | No | string | 115 NetDisk Application ID | +| `cloud115_key` | No | string | 115 NetDisk Application Secret | +| `googleui_uid` | No | string | Google Client ID | +| `googleui_key` | No | string | Google Global API Key | +| `yandexui_uid` | No | string | Yandex Application ID | +| `yandexui_key` | No | string | Yandex Application Secret | +| `dropboxs_uid` | No | string | Dropbox Application ID | +| `dropboxs_key` | No | string | Dropbox Application Secret | +| `quarkpan_uid` | No | string | QuarkPan Application ID | +| `quarkpan_key` | No | string | QuarkPan Application Secret | + +## Community APIPages + +:::tip +The following servers are built and provided by community volunteers. Once used, user authorization credentials will inevitably be sent to the servers over the network. This project is licensed under AGPLv3 and only guarantees to provide the source code "as is". Users should verify the deployed content at their own discretion. +::: + +- 国际站点:[api.oplist.org](https://api.oplist.org/) +- 中国大陆:[api.oplist.org.cn](https://api.oplist.org.cn/) + +...... + +[Welcome to submit more community servers](https://github.com/OpenListTeam/OpenList-Docs/pulls) diff --git a/pages/en/ecosystem/official_desktop.md b/pages/en/ecosystem/official_desktop.md new file mode 100644 index 000000000..1ffce1bd3 --- /dev/null +++ b/pages/en/ecosystem/official_desktop.md @@ -0,0 +1,194 @@ +--- +categories: + - ecosystem + - eco_official +top: 980 +--- + +# OpenList Desktop + +## What is OpenList Desktop + +### [OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) + +[OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) is a powerful cross-platform desktop application led by [@Kuingsmile](https://github.com/Kuingsmile) and collaboratively developed with other [main contributors](https://github.com/OpenListTeam/OpenList-Desktop/graphs/contributors). Built with Vue 3, Tauri, and Rust, it provides a user-friendly interface for managing OpenList services and performing local mounts via Rclone. The application serves as a comprehensive solution for managing OpenList file management services, mounting and managing cloud storage (WebDAV), monitoring service status and uptime, and providing system tray integration for background operations. + +## Features + +### Core Features + +- **OpenList Service Management**: Start, stop, and monitor OpenList core services +- **Local Mounting**: Mount via Rclone to the local file system +- **Real-time Monitoring**: Track service status, uptime, and performance metrics +- **Process Management**: Advanced process control with auto-restart capabilities +- **System Tray**: Background operation with system tray notifications + +### Management Features + +- **Service Control**: Start/stop/restart OpenList and Rclone services +- **Configuration Management**: GUI-based configuration for all services +- **Log Monitoring**: Real-time log viewing and management +- **Update Management**: Automatic update checking and installation +- **Auto-startup**: Configure applications to start with system boot + +## Installation + +### System Requirements + +- **Operating System**: Windows 10+, macOS 10.15+, or Linux (Ubuntu 18.04+) + +### Download Options + +#### GitHub Releases (Recommended) + +Download the latest release from [GitHub Releases](https://github.com/OpenListTeam/OpenList-Desktop/releases): + +- **Windows**: `OpenList-Desktop_x.x.x_{arch}-setup.exe` +- **macOS**: `OpenList-Desktop_x.x.x_{arch}.dmg` +- **Linux**: `OpenList-Desktop_x.x.x_{arch}.deb` or `OpenList-Desktop_x.x.x_{arch}.rpm` + +#### Build from Source + +```bash +# Clone the repository +git clone https://github.com/OpenListTeam/OpenList-Desktop.git +cd openlist-desktop + +# Install dependencies +yarn install + +# Prepare development environment +yarn run prebuild:dev + +# Build the application +yarn run build +yarn run tauri build +``` + +### Installation Steps + +#### Windows + +##### Using Installer + +1. Download the `.exe` installer +2. Run the installer as Administrator +3. Follow the installation wizard +4. Launch from Start Menu or Desktop shortcut + +##### Using Winget + +```bash +winget install OpenListTeam.OpenListDesktop +``` + +#### macOS + +1. Download the `.dmg` file +2. Open the DMG and drag OpenList Desktop to Applications +3. Right-click and select "Open" (first time only) +4. Grant necessary permissions when prompted + +#### Linux + +1. Download the `.deb` or `.rpm` package +2. Use your package manager to install: + + ```bash + sudo dpkg -i OpenList-Desktop_x.x.x_amd64.deb + # or + sudo rpm -i OpenList-Desktop_x.x.x_amd64.rpm + ``` + +## Usage + +### First Launch + +It is recommended to run OpenList Desktop with Administrator privileges on first launch to ensure proper service installation and configuration. + +1. **Initial Setup**: On first launch, the application will guide you through initial configuration +2. **Service Installation**: Install the OpenList service when prompted +3. **Storage Configuration**: Configure your first cloud storage connection + +### Basic Operations + +#### Starting Services + +```bash +Dashboard → Quick Actions → Start OpenList Core +Dashboard → Quick Actions → Start Rclone Backend +``` + +#### Adding Cloud Storage + +1. Navigate to **Mount** tab +2. Click **Add Remote** button +3. Configure storage settings: + - **Name**: Unique identifier for your storage + - **Type**: Storage provider (WebDAV) + - **URL**: Storage endpoint URL + - **Credentials**: Username and password + - **Mount Point**: Local directory path +4. Click **Save** and **Mount** + +#### Monitoring Operations + +- **Service Status**: Check the dashboard for service health indicators +- **Logs**: Use the Logs tab to monitor system operations +- **Performance**: View uptime and response metrics on the dashboard + +## Local Development + +### Prerequisites + +- **Node.js**: v22+ with yarn +- **Rust**: Latest nightly version +- **Git**: Version control + +1. **Clone the repository** + + ```bash + git clone https://github.com/OpenListTeam/OpenList-Desktop.git + cd openlist-desktop + ``` + +2. **Install dependencies** + + ```bash + yarn install + ``` + +3. **Prepare development environment** + + ```bash + yarn run prebuild:dev + ``` + +4. **Start development server** + + ```bash + yarn tauri dev + ``` + +### Building + +```bash +# Build the application +yarn run build +yarn run tauri build +``` + +## License & Legal + +### License + +This desktop application project is licensed under the **[GNU General Public License v3.0 (GPL-3.0)](https://www.gnu.org/licenses/gpl-3.0.en.html)**. + +- **Freedom to Use**: You can use, modify, and distribute this application +- **Copyleft**: Any derivative works must also be licensed under GPL-3.0 +- **Source Code**: You must provide source code when distributing the application +- **Attribution**: You must preserve copyright notices and license information + +For the full license text, see the [LICENSE](https://github.com/OpenListTeam/OpenList-Desktop/blob/main/LICENSE) file. + +By contributing to this project, you agree that your contributions will be licensed under the same GPL-3.0 license. diff --git a/pages/en/ecosystem/official_docs.md b/pages/en/ecosystem/official_docs.md new file mode 100644 index 000000000..eab7c6564 --- /dev/null +++ b/pages/en/ecosystem/official_docs.md @@ -0,0 +1,112 @@ +--- +categories: + - ecosystem + - eco_official +top: 990 +--- + +# OpenList Docs + +## What is OpenList Docs + +### [OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) + +[OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) is the official documentation website for OpenList, led by [@cxw620](https://github.com/cxw620) and collaboratively developed with other [main contributors](https://github.com/OpenListTeam/OpenList-Docs/graphs/contributors). Built with VitePress, it provides comprehensive documentation for OpenList, including installation guides, configuration instructions, API references, and ecosystem information. The documentation supports multiple languages and features real-time builds from the GitHub repository. + +## How to use OpenList Docs + +You can directly access [doc.oplist.org.cn](https://doc.oplist.org.cn/) to view the documentation. + +## Local Development + +### Prerequisites + +- **Node.js** 24 or later (see `.nvmrc`) +- **Bun**: package manager and script runner +- **Git**: for version control and cloning the repository + +1. **Clone the repository** + + ```bash + git clone https://github.com/OpenListTeam/OpenList-Docs.git + cd OpenList-Docs + ``` + +2. **Install dependencies** + + ```bash + bun install + ``` + +3. **Start development server** + + ```bash + bun run dev + ``` + +4. **Open in browser** + + The documentation site will be available at `http://localhost:5173` + +### Building + +```bash +# Build static site into dist/ +bun run build + +# Preview build +bun run preview +``` + +### Writing Tips + +The site is built with [VitePress](https://vitepress.dev/). Everything in its [Markdown guide](https://vitepress.dev/guide/markdown) works here. + +1. **One file per language**: Chinese pages live in `pages/`, English pages live in `pages/en/` under the same relative path, for example `pages/guide/drivers/s3.md` and `pages/en/guide/drivers/s3.md`. When you change a page, update both files. + +2. **Page title**: The first line of the page content is the title, written as a level-1 heading (`# Title`). Do not add a `title` field to the frontmatter. + +3. **Sidebar**: A page appears in the sidebar when its frontmatter has `categories`. Pages are sorted by `top`, from high to low: + + ```md + --- + top: 895 + categories: + - guide + - drivers + --- + + # S3 + ``` + +4. **Nested containers**: When a container such as `::: tip` contains another container, the outer one needs more colons, otherwise the first `:::` closes the outer container: + + ```md + :::: details Example + ::: tip + Inner tip + ::: + :::: + ``` + +5. **Light and dark images**: Add `#light` / `#dark` to image links to show them only in the matching color mode: + + ```md + ![Screenshot](/img/example-light.png#light) + ![Screenshot](/img/example-dark.png#dark) + ``` + +## License & Legal + +### License + +This documentation project is licensed under the **[GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html)**. + +- **Freedom to Use**: You can use, modify, and distribute this documentation +- **Copyleft**: Any derivative works must also be licensed under AGPL-3.0 +- **Network Use**: If you run a modified version on a server, you must provide the source code +- **Attribution**: You must preserve copyright notices and license information + +For the full license text, see the [LICENSE](https://github.com/OpenListTeam/OpenList-Docs/blob/main/LICENSE) file. + +By contributing to this project, you agree that your contributions will be licensed under the same AGPL-3.0 license. diff --git a/pages/en/ecosystem/official_mobile.md b/pages/en/ecosystem/official_mobile.md new file mode 100644 index 000000000..f505501d4 --- /dev/null +++ b/pages/en/ecosystem/official_mobile.md @@ -0,0 +1,70 @@ +--- +categories: + - ecosystem + - eco_official +top: 975 +--- + +# OpenList Mobile + +## What is OpenList Mobile + +### [OpenListTeam/OpenList-Mobile](https://github.com/OpenListTeam/OpenList-Mobile) + +[OpenListTeam/OpenList-Mobile](https://github.com/OpenListTeam/OpenList-Mobile) is a mobile file server application based on [OpenList](https://github.com/OpenListTeam/OpenList), built with Flutter framework. It turns your phone into a powerful file server with LAN file sharing, remote access, and online management capabilities. The application is collaboratively developed by the [main contributors](https://github.com/OpenListTeam/OpenList-Mobile/graphs/contributors) and features automatic updates that sync with the latest OpenList releases daily. + +## Features + +- **Mobile File Server**: Turn your mobile device into a file server +- **LAN File Sharing**: Share files within local network +- **Remote Access**: Access your files with internet connection +- **Online Management**: Web-based file management interface +- **Auto-Update**: Automatically syncs with latest OpenList releases + +## Installation + +### System Requirements + +- **Storage**: Minimum 100MB free space +- **Permissions**: Storage access permission required + +### Download Options + +#### GitHub Releases (Recommended) + +Download the latest release from [GitHub Releases](https://github.com/OpenListTeam/OpenList-Mobile/releases): + +- **Stable Release**: [Latest Release](https://github.com/OpenListTeam/OpenList-Mobile/releases/latest) - Recommended for production use +- **Development Build**: [Development Build](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml) - Latest features and fixes + +#### Auto-Update Feature + +**Auto-Update**: [GitHub Actions](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml) automatically checks for the latest [OpenList](https://github.com/OpenListTeam/OpenList/releases) version twice daily (5 AM & 5 PM) and builds releases, ensuring you always have access to the latest version. + +### Installation Steps + +#### Android + +1. Download the `.apk` file from releases +2. Enable "Install from unknown sources" in Android settings +3. Install the APK file + +#### iOS + +iOS support is currently experimental and still in development. + +## License & Legal + +### License + +This mobile application project is licensed under the **[GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html)**. + +- **Freedom to Use**: You can use, modify, and distribute this application +- **Copyleft**: Any derivative works must also be licensed under AGPL-3.0 +- **Network Use**: If you run a modified version on a server, you must provide source code to users +- **Source Code**: You must provide source code when distributing the application +- **Attribution**: You must preserve copyright notices and license information + +For the full license text, see the [LICENSE](https://github.com/OpenListTeam/OpenList-Mobile/blob/main/LICENSE) file. + +By contributing to this project, you agree that your contributions will be licensed under the same AGPL-3.0 license. diff --git a/pages/en/ecosystem/official_proxy.md b/pages/en/ecosystem/official_proxy.md new file mode 100644 index 000000000..00511b970 --- /dev/null +++ b/pages/en/ecosystem/official_proxy.md @@ -0,0 +1,87 @@ +--- +categories: + - ecosystem + - eco_official +top: 960 +--- + +# OpenList Proxy + +## What is OpenList Proxy + +[**OpenList Proxy**](https://github.com/OpenListTeam/OpenList-Proxy) is a simple implementation for proxying OpenList's **download traffic**. With this tool, you can isolate the server traffic of the OpenList deployment from the traffic required for downloads, thereby reducing the traffic consumption of the main server or speeding up downloads. + +## How to use OpenList Proxy + +:::danger +Cloudflare has explicitly prohibited the use of Workers for proxy operations. Quick deployments implemented on CF-Worker should be used for **experimental** and **temporary testing** purposes only, and not for long-term or high-traffic. + +OpenList is not responsible for any consequences resulting from the use + +::: + +For OpenList Proxy, we provide two deployment methods: + +- cf-worker +- Binary File Deployment + +### Cloudflare Worker + +:::tip +In the new version, environment-based configuration has been introduced. Please configure the environment variables as required after deployment. + +Do not use "/" at the end of the address. + +::: + +- Simple Worker Deployment Tutorial On the Cloudflare homepage, select "Workers and Pages", + then click "Create" and choose "Start from Hello World!". + After deployment, select "Edit Code", go to here, replace the code, and click "Deploy" again. + +- (Optional) Configure Domain Go to the Worker configuration page, click "Settings", then click "Add" next to Domains and Routes. Enter the configured subdomain. Use a CNAME record for the corresponding subdomain to point to the workers.dev domain. + +- Configure Environment Variables Go to the Worker configuration page, + click "Settings", then select "Add" next to Variables and Secrets. + Copy the following into the variable names: + + ```env + ADDRESS=https://your-openlist-server.com + TOKEN=your-api-token-here + WORKER_ADDRESS=https://your-worker-address + DISABLE_SIGN=false + ``` + +- ADDRESS is the address of your OpenList instance, only ports 443 and 80 are supported. + WORKER_ADDRESS is the address of the Worker. If you have bound a custom domain, use the custom domain. This is also the proxy address needed in OpenList. + If DISABLE_SIGN is set to true, Proxy will not verify signatures, and anyone who knows the file path and Proxy address can access the file. Please use with caution. + It is recommended to set TOKEN as a secret type. In OpenList, go to Settings → Others at the bottom; this token is long-term valid and has full permissions for OpenList. + +- CDN Configuration Suggestion: Keep ADDRESS and WORKER_ADDRESS configured as the origin addresses. + +### Binary File Deployment + +Download the [binary package](https://github.com/OpenListTeam/OpenList-Proxy/releases) and run the command `./openlist-proxy -help` to learn how to use it. + +## How OpenList Proxy Works + +OpenList Proxy works by proxying the OpenList API to isolate the download traffic of OpenList. Its working principle is as follows: + +```mermaid +sequenceDiagram + participant Client as Client + participant Proxy as Cloudflare Worker + participant OpenList as OpenList Server + participant Storage as File Storage + + Client->>Proxy: Download Request (with signature) + Proxy->>Proxy: Verify signature and expiration time + Proxy->>OpenList: Request file link (/api/fs/link) + OpenList-->>Proxy: Return real download address + Proxy->>Storage: Request file content + Storage-->>Proxy: Return file stream + Proxy-->>Client: Forward file content +``` + +Proxy verifies the signature and expiration time to ensure the legitimacy of the request. It then requests the file link from the OpenList server to obtain the real download address. After that, Proxy requests the file storage service for the file content and forwards it to the client. + +Proxy's signature can be disabled by setting the `DISABLE_SIGN` environment variable or flag. If signature verification is disabled, Proxy will not check the signature and expiration time, allowing anyone who knows the file path and Proxy address to bypass OpenList's own signature verification (which can be configured in the management interface) to access the file. Please use this feature with caution. diff --git a/pages/en/ecosystem/official_worker/about.md b/pages/en/ecosystem/official_worker/about.md new file mode 100644 index 000000000..6f46330ae --- /dev/null +++ b/pages/en/ecosystem/official_worker/about.md @@ -0,0 +1,87 @@ +--- +categories: + - ecosystem + - eco_worker +top: 971 +--- + +# FAQ + +## FAQ + +### The install wizard doesn't appear on first visit + +Make sure `ADMIN_PASS` is **not** set — when it is set, the worker auto-initializes and skips the wizard. Clear the variable and redeploy, then revisit the site. + +--- + +### Cloudflare reports "unable to fetch repository content" + +You are trying to deploy directly from the upstream `OpenListTeam/OpenList-Worker` repository. Cloudflare Workers only allows deploying from repositories you own. [Fork](https://github.com/OpenListTeam/OpenList-Worker/fork) the project first, then connect your fork. + +--- + +### Settings are lost after redeployment / page refresh + +This usually means no persistent storage is configured. Check: + +1. `DB_DRIVER` is set (not `memory`) +2. The corresponding binding (KV / D1 / Blob) is correctly bound in the platform dashboard +3. For EdgeOne, the default `auto` driver should auto-detect Blob — if not, explicitly set `DB_DRIVER=blob` + +--- + +### How do I reset the admin password? + +If you can still log in, go to **Management → Users** to change the password. + +If you are locked out: + +1. Set the `ADMIN_PASS` environment variable to a new password and redeploy. +2. After logging in, remove the variable and redeploy again to re-enable the install wizard on next cold start (or leave it set as a permanent password). + +--- + +### CORS errors when accessing the API from a custom domain + +Add `ALLOW_URLS` as an environment variable with a comma-separated list of allowed origins, e.g.: + +``` +ALLOW_URLS=https://your-domain.com,https://www.your-domain.com +``` + +--- + +### `DB_FORMAT=sql` tables are not created automatically + +D1 tables are created via Drizzle migrations on first startup. Make sure: + +1. The D1 binding (`DB`) is correctly configured in `wrangler.toml` or the dashboard. +2. After binding, trigger a cold start by redeploying. + +If using **Automatic resource provisioning**, omit `database_id` from the D1 binding and Wrangler (>= 4.45.0) will create the database automatically on deploy. + +--- + +### How do I migrate data from the Go backend to OpenList Worker? + +Use `DB_FORMAT=sql` + `DB_DRIVER=d1` (or `mysql`) with the fixed `x_` table prefix. The TS Worker and the Go backend share the same table schema, so you can: + +1. Export the Go backend's SQLite database. +2. Import it into a Cloudflare D1 database via the Cloudflare dashboard or `wrangler d1 execute`. +3. Configure the Worker to point to the same D1 database. + +--- + +### ESA EdgeKV settings revert after a few seconds + +This is caused by EdgeKV's eventual consistency. The Worker implements a module-level cache with a 60-second TTL to mitigate this. If the issue persists, wait ~60 seconds for the cache to expire and the setting to propagate across nodes. + +--- + +### Build fails with "pnpm: command not found" + +The deploy platform is using npm by default. Either: + +- Set the **install command** to `npm install --legacy-peer-deps` and **build command** to `npm run build`, or +- Enable pnpm in the platform settings (e.g. Cloudflare Workers → Framework preset → set Node version to 18+) diff --git a/pages/en/ecosystem/official_worker/basic.md b/pages/en/ecosystem/official_worker/basic.md new file mode 100644 index 000000000..c8452c819 --- /dev/null +++ b/pages/en/ecosystem/official_worker/basic.md @@ -0,0 +1,103 @@ +--- +categories: + - ecosystem + - eco_worker +top: 978 +--- + +# Architecture + +## Design Architecture + +OpenList Worker is a Serverless-first rewrite of the OpenList Go backend in TypeScript. The system is divided into three layers: edge runtime, data access, and frontend static assets. + +## Tech Stack + +### Backend + +| Component | Technology | Description | +| :------------- | :------------------------------------------- | :----------------------------------------- | +| HTTP framework | [Hono.js](https://hono.dev/) | Lightweight, edge-native web framework | +| Runtime | Cloudflare Workers / EdgeOne Functions / ESA | Edge compute platforms | +| Language | TypeScript | Fully typed, compiled via esbuild / Vite | +| ORM | Drizzle ORM | Type-safe SQL query builder for D1 / MySQL | +| Build tool | esbuild / Vite | Single-file Worker bundle | + +### Frontend + +| Component | Technology | Description | +| :----------- | :----------------------- | :------------------------------------- | +| Framework | SolidJS + TypeScript | Reactive SPA frontend | +| UI library | Hope UI (@hope-ui/solid) | Component library | +| Build tool | Vite | Fast frontend build | +| Bundled with | Workers Static Assets | Served from the same origin as the API | + +## Data Storage + +### Storage Format (`DB_FORMAT`) + +The `DB_FORMAT` variable controls how data is serialized: + +| Value | Description | Best for | +| :-------------- | :---------------------------------------------------- | :------------------------------------- | +| `map` (default) | Whole object serialized as a single JSON value | KV / Blob storage | +| `key` | Per-key storage, one record per entity | KV with high read frequency | +| `sql` | Relational tables, identical schema to the Go backend | D1 / MySQL — enables Go ↔ TS migration | + +### Storage Driver (`DB_DRIVER`) + +The `DB_DRIVER` variable selects the physical storage backend: + +| Value | Platform | Description | +| :--------------- | :----------------- | :------------------------------------------ | +| `auto` (default) | Universal | Auto-detect: blob → cfkv → kv → d1 → memory | +| `blob` | EdgeOne / ESA | EdgeOne Blob or Alibaba ESA Blob | +| `cfkv` | Universal | Cloudflare KV via REST API (cross-platform) | +| `kv` | Cloudflare Workers | Cloudflare KV binding | +| `d1` | Cloudflare Workers | Cloudflare D1 (SQLite) | +| `do` | Cloudflare Workers | Durable Objects (strong consistency) | +| `mysql` | Node.js container | External MySQL / MariaDB | + +### SQL Table Alignment with Go Backend + +When `DB_FORMAT = "sql"`, the TS Worker uses the same table names and schema as the Go backend (GORM, default prefix `x_`), so the two backends can share the same physical database: + +| Go struct | Table name | +| :------------ | :---------------- | +| `SettingItem` | `x_setting_items` | +| `SharingDB` | `x_sharing_dbs` | +| `Storage` | `x_storages` | +| `User` | `x_users` | +| `Meta` | `x_metas` | +| (TS only) | `x_plugins` | + +The prefix is fixed to `x_`, matching the Go backend default. + +## Project Structure + +``` +OpenList-Worker/ +├── src/ +│ ├── backend/ # Hono.js Worker entry & backend logic +│ │ ├── worker.ts # Cloudflare Workers entry +│ │ ├── drivers/ # Storage driver implementations (kv / d1 / blob / mysql …) +│ │ ├── server/ # Route registrations & middleware +│ │ ├── pkg/ # Shared utilities & helpers +│ │ └── internal/ # Core business logic (auth, storage, meta, …) +│ └── frontend/ # Built-in frontend (SolidJS + Vite) +├── dist/ # Build output (Worker bundle + frontend assets) +├── esa-entry.ts # Alibaba Cloud ESA entry +├── wrangler.toml # Cloudflare Workers configuration +├── esa.jsonc # Alibaba Cloud ESA configuration +├── edgeone.json # EdgeOne schedules configuration +└── package.json +``` + +## Supported Platforms + +| Platform | Entry | Persistence | Notes | +| :-------------------- | :------------------------- | :----------- | :------------------------- | +| Cloudflare Workers | `worker.ts` | D1 / KV / DO | One-click deploy supported | +| Tencent Cloud EdgeOne | `worker.ts` | Blob / KV | One-click deploy supported | +| Alibaba Cloud ESA | `esa-entry.ts` | EdgeKV | Manual build & deploy | +| Node.js container | `worker.ts` (with adapter) | MySQL | Self-hosted | diff --git a/pages/en/ecosystem/official_worker/guide.md b/pages/en/ecosystem/official_worker/guide.md new file mode 100644 index 000000000..c8c0e7e86 --- /dev/null +++ b/pages/en/ecosystem/official_worker/guide.md @@ -0,0 +1,64 @@ +--- +categories: + - ecosystem + - eco_worker +top: 977 +--- + +# Deployment + +## How to Deploy + +For a detailed, step-by-step deployment guide (Cloudflare Workers / EdgeOne / ESA), see [OpenList Worker 部署指南](/en/guide/installation/worker). + +### One-click Deploy + +| EdgeOne 国际站 | EdgeOne 中国站 | Cloudflare Workers | +| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://console.cloud.tencent.com/edgeone/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/OpenListTeam/OpenList-Worker) | + +> **Note**: If Cloudflare reports "unable to fetch repository content", [Fork](https://github.com/OpenListTeam/OpenList-Worker/fork) the project first, then deploy via the GitHub repository connection. + +For platform-specific step-by-step guides, see: + +- [Cloudflare Workers](./guide_cfw) +- [Tencent Cloud EdgeOne](./guide_eom) +- [Alibaba Cloud ESA](./guide_esa) + +### Initialization + +::: tip +After deployment, the first visit to the site automatically enters an **install wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. +::: + +### Local Development + +**Prerequisites** + +- Node.js 18+ (pnpm recommended) +- A Cloudflare account (for deploying to Workers) + +**Local development** + +```bash +# 1. Install dependencies +pnpm install + +# 2. Configure wrangler.toml (fill in JWT_SECRET, KV/D1 bindings) + +# 3. Start the dev server (auto fetch the official frontend and run the Worker) +pnpm run dev:unified + +# or run the Worker only (frontend built separately) +pnpm run dev:worker +``` + +**Production deploy** + +```bash +# One-click deploy: ensure KV namespace exists → fetch official frontend → deploy to Cloudflare Workers +pnpm run deploy + +# or deploy the Worker directly (skip KV check and frontend build) +pnpm run deploy:worker +``` diff --git a/pages/en/ecosystem/official_worker/guide_cfw.md b/pages/en/ecosystem/official_worker/guide_cfw.md new file mode 100644 index 000000000..b31f34bae --- /dev/null +++ b/pages/en/ecosystem/official_worker/guide_cfw.md @@ -0,0 +1,107 @@ +--- +categories: + - ecosystem + - eco_worker +top: 967 +--- + +# Deploy to Cloudflare Workers + +## Deploy to Cloudflare Workers + +### Prerequisites + +- A [Cloudflare](https://dash.cloudflare.com/) account +- A [GitHub](https://github.com/) account (for connecting the repository) +- Node.js 18+ and pnpm (only required for local / Wrangler deploy) + +### Deploy methods + +You have two options to deploy to Cloudflare Workers: + +1. **One-click deploy** — click the **Deploy to Cloudflare Workers** button above. +2. **Manual create via GitHub** — follow the steps below. + +### Step 1: Create an application & connect GitHub + +Open the Cloudflare Workers dashboard, click **Create application** in the top-right, and choose **Connect to GitHub**. Cloudflare will prompt you to authorize access to your GitHub account — click **Authorize** and select the account (or organization) that contains your fork. + +![Create an application — connect GitHub](/img/worker/create_app1.png) + +### Step 2: Select the repository + +Fork this project to your own GitHub account first, then select your forked Worker repository when creating the application. + +::: tip +If you deploy directly using the upstream `OpenListTeam/OpenList-Worker` repository and Cloudflare reports "unable to fetch repository content", fork the project first and select your fork instead. +::: + +![Select the forked repository](/img/worker/create_app2.png) + +### Step 3: Create the application + +Click **Create**. Keep the default build parameters and commands: + +- **Framework preset**: None / Workers +- **Build command**: `pnpm run build` +- **Deploy command**: `npx wrangler deploy` +- **Production branch**: `main` + +Cloudflare will build the Worker and deploy it to a `*.workers.dev` subdomain. + +![Keep default build parameters](/img/worker/create_app3.png) + +### Step 4: Configure environment variables + +Enter the Worker project you just created, open **Settings → Variables and secrets** (or **Runtime variables and secrets**), and add the environment variables. + +![Add environment variables](/img/worker/create_app4.png) + +The required variables (`DB_FORMAT`, `DB_DRIVER`) and their optional combinations are described in the [Environment Variables](#environment-variables) section at the end of this page. + +### Step 5: Bind the storage binding + +If you selected KV or D1 as the driver, you need to bind the corresponding binding in **Settings → Bindings**: + +| Type | Variable name | +| ---- | ------------- | +| `d1` | `DB` | +| `kv` | `KV` | +| `do` | `DO` | + +![Bind the KV / D1 / DO binding](/img/worker/create_app5.png) + +::: tip Automatic D1 provisioning +For D1, you can enable Cloudflare's **Automatic resource provisioning**: omit `database_id` in the D1 binding, and Wrangler (>= 4.45.0) auto-creates a D1 database with the same name and writes back the ID on deploy. +::: + +### Step 6: Bind a custom domain + +Add your own domain in **Settings → Domains and Routes**, then create a CNAME record for the subdomain pointing to your `*.workers.dev` domain. + +![Bind a custom domain](/img/worker/create_app6.png) + +### After deployment + +::: tip +After deployment, the first visit to the site automatically enters an **install wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. +::: + +### Local / Wrangler deploy (alternative) + +If you prefer deploying via the command line: + +```bash +# 1. Clone and install dependencies +git clone https://github.com/OpenListTeam/OpenList-Worker.git +cd OpenList-Worker +pnpm install + +# 2. Configure wrangler.toml (JWT_SECRET, KV / D1 bindings) + +# 3. Deploy to Cloudflare Workers +pnpm run deploy +# or: pnpm run deploy:worker (skip frontend build) +``` + +For a full list of environment variables and recommended configurations, see [Environment Variables](./guide_env). diff --git a/pages/en/ecosystem/official_worker/guide_env.md b/pages/en/ecosystem/official_worker/guide_env.md new file mode 100644 index 000000000..ead96b05d --- /dev/null +++ b/pages/en/ecosystem/official_worker/guide_env.md @@ -0,0 +1,95 @@ +--- +categories: + - ecosystem + - eco_worker +top: 972 +--- + +# Environment Variables + +## Environment Variables + +### Required variables + +The following parameters are required: + +| Parameter | Optional values | Description | +| ----------- | ----------------------------------------------------------------- | ----------------------- | +| `DB_FORMAT` | `map` (default) / `key` / `sql` | Data persistence format | +| `DB_DRIVER` | `auto` (default) / `blob` / `cfkv` / `kv` / `d1` / `do` / `mysql` | Storage location | + +#### Security variables + +In addition to `DB_FORMAT` and `DB_DRIVER`, it is strongly recommended to configure the following security-related variables: + +| Variable | Required | Description | +| ------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `JWT_SECRET` | Recommended | JWT signing key (≥16 chars), also used for data encryption and cron task auth. Auto-generated and persisted to KV when unset | +| `ADMIN_PASS` | Optional | Skip the install wizard and auto-initialize the admin with this password | +| `ALLOW_URLS` | Optional | Comma-separated CORS allowlist | + +#### Other optional variables + +| Variable | Default | Description | +| ------------ | ---------- | ---------------------------------------------------------------- | +| `MAX_UPLOAD` | `26214400` | Max size (bytes) for a whole upload (`/put`, `/form`) | +| `MAX_UPPART` | `16777216` | Max size (bytes) per chunk in multipart upload | +| `ASSET_URLS` | — | Frontend asset CDN base URL, supports the `$version` placeholder | +| `ALLOW_SEED` | — | Allowlist of hosts permitted as seed-data sources | + +### Storage format differences + +| `DB_FORMAT` | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `map` | All data stored as a single JSON. Reads the whole object on every access — lower performance but best compatibility. | +| `key` | Each record stored as a Key-Value pair, read on demand — decent compatibility and performance, but no indexing. | +| `sql` | Data stored as SQL tables, fully identical to the Go backend. Supports indexing and full features, but multiple reads take longer. | + +### Storage driver differences + +| `DB_DRIVER` | Supported platform | Description | +| ----------- | ------------------ | -------------------------------------------------------------------------- | +| `auto` | Universal | Auto-detect, priority: blob → cfkv → kv → d1 → memory | +| `blob` | EdgeOne / ESA | Persistence provided by EdgeOne or Alibaba ESA Blob, free | +| `cfkv` | Universal | Cloudflare KV REST API, for remote access on platforms without persistence | +| `kv` | CF / EO / ESA | KV database, fast and free | +| `d1` | CF | D1 database, Cloudflare only | +| `do` | CF | Durable Objects, Cloudflare only | +| `mysql` | Universal | Connect to your own MySQL database | + +### Valid combinations + +| `DB_FORMAT` | `DB_DRIVER` | Description | +| ----------- | ----------- | --------------------------------------------------------------------- | +| `map` | `auto` | JSON storage, auto-selects blob / kv / d1 based on the platform | +| `map` | `blob` | JSON storage via EdgeOne / ESA Blob (recommended for those platforms) | +| `map` | `cfkv` | JSON storage via remote CF KV outside CF Workers | +| `map` | `kv` | JSON storage via KV on CF Workers (recommended) | +| `map` | `d1` | JSON storage via D1 on CF Workers (not recommended) | +| `map` | `do` | JSON storage via DO on CF Workers (not recommended, may incur cost) | +| `key` | `kv` | Key-Value storage via KV on CF Workers (recommended) | +| `key` | `d1` | Key-Value storage via D1 on CF Workers (recommended) | +| `key` | `mysql` | Key-Value storage via MySQL on CF Workers (not recommended) | +| `sql` | `d1` | SQL-Table storage via D1 on CF Workers (recommended) | +| `sql` | `mysql` | SQL-Table storage via MySQL | + +### Remote database configuration + +If you bound `cfkv`, set the following variables: + +| Variable | Description | +| ------------ | ----------------------------------- | +| `CF_ACCOUNT` | Cloudflare account ID | +| `CF_KV_UUID` | Cloudflare KV namespace ID | +| `CF_API_KEY` | Cloudflare API token with KV access | + +If you bound MySQL, set the following variables: + +| Variable | Description | +| ------------ | ------------------------------------------------------------------------------------------------ | +| `MYSQL_URL` | Connection string, e.g. `mysql://user:pass@host:3306/db`. When set, the fields below are ignored | +| `MYSQL_HOST` | Database host | +| `MYSQL_PORT` | Database port (`3306`) | +| `MYSQL_USER` | Database user | +| `MYSQL_PASS` | Database password | +| `MYSQL_NAME` | Database name | diff --git a/pages/en/ecosystem/official_worker/guide_eom.md b/pages/en/ecosystem/official_worker/guide_eom.md new file mode 100644 index 000000000..ea6d9db73 --- /dev/null +++ b/pages/en/ecosystem/official_worker/guide_eom.md @@ -0,0 +1,76 @@ +--- +categories: + - ecosystem + - eco_worker +top: 966 +--- + +# Deploy to Tencent EdgeOne + +## Deploy to EdgeOne + +### Prerequisites + +- A [Tencent Cloud EdgeOne](https://console.edgeone.ai/makers) account +- A GitHub account (for connecting the repository) + +### Step 1: Deploy the application + +Click the **EdgeOne** deploy button above and choose the international or China site: + +- [International console](https://console.edgeone.ai/makers) +- [China console](https://console.cloud.tencent.com/edgeone/makers) + +### Step 2: Set environment variables + +Click **New application**, select the forked repository, configure the variables, then click **Start deployment**: + +![Select the forked repository and set variables](/img/worker/edgeone_ui1.png) + +Required variables (`DB_FORMAT`, `DB_DRIVER`) are described in the [Environment Variables](#environment-variables) section at the end of this page. + +::: tip Persistence +EdgeOne Makers uses `@edgeone/pages-blob` for persistence. The default `auto` driver auto-detects Blob, so no extra configuration is needed. You can also explicitly set: + +- `DB_DRIVER = "blob"` with `DB_FORMAT = "map"` (JSON storage) +- `DB_DRIVER = "kv"` with `DB_FORMAT = "key"` (Key-Value storage) + ::: + +### Step 3: Bind KV storage + +If you use KV storage in the environment variables, you must bind the `KV` variable to your namespace under **Storage → KV Storage**: + +![Bind the KV namespace](/img/worker/edgeone_ui3.png) + +### Step 4: Configure a custom domain + +After deployment, open **Domain management**, add a custom domain, then set up the CNAME record as required and enable SSL: + +![Add a custom domain and enable SSL](/img/worker/edgeone_ui2.png) + +### Step 5: Scheduled tasks + +EdgeOne supports scheduled refresh via `edgeone.json`. Set `cron_secret` in the payload to your `JWT_SECRET` value and configure the schedule: + +```jsonc +{ + "schedules": [ + { + "name": "token-refresh", + "cron": "0 2 * * *", + "path": "/api/task/refresh", + "method": "POST", + "payload": { "cron_secret": "" }, + "timezone": "Asia/Shanghai", + }, + ], +} +``` + +### After deployment + +::: tip +After deployment, the first visit to the site automatically enters an **install wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. +::: + +For a full list of environment variables and recommended configurations, see [Environment Variables](./guide_env). diff --git a/pages/en/ecosystem/official_worker/guide_esa.md b/pages/en/ecosystem/official_worker/guide_esa.md new file mode 100644 index 000000000..6cc16acb6 --- /dev/null +++ b/pages/en/ecosystem/official_worker/guide_esa.md @@ -0,0 +1,44 @@ +--- +categories: + - ecosystem + - eco_worker +top: 965 +--- + +# Deploy to Alibaba Cloud ESA + +## Deploy to Alibaba Cloud ESA + +OpenList Worker ships a dedicated ESA edge function entry (`esa-entry.ts`), which adapts Alibaba Cloud EdgeKV into the project's KV interface. + +### Build & deploy + +```bash +# 1. Install dependencies +pnpm install + +# 2. Build (produces dist/esa-entry.js) +pnpm run build +``` + +`esa.jsonc` defines the edge function entry, install/build commands, and the static assets directory: + +```jsonc +{ + "name": "openlist", + "entry": "./dist/esa-entry.js", + "installCommand": "pnpm install --no-frozen-lockfile", + "buildCommand": "pnpm run build", + "assets": { "directory": "./dist" }, +} +``` + +### KV namespace + +Configure the EdgeKV namespace via the `KV_NAMESPACE` environment variable (default `openlist`). + +:::tip +ESA EdgeKV is eventually consistent. The entry implements a module-level TTL cache (60s) to avoid "saved settings revert after refresh" caused by cross-node sync delay. +::: + +For a full list of environment variables and recommended configurations, see [Environment Variables](./guide_env). diff --git a/pages/en/ecosystem/official_worker/index.md b/pages/en/ecosystem/official_worker/index.md new file mode 100644 index 000000000..bcf5e0485 --- /dev/null +++ b/pages/en/ecosystem/official_worker/index.md @@ -0,0 +1,73 @@ +--- +categories: + - ecosystem + - eco_worker +top: 979 +--- + +# Intro + +## What is OpenList Worker + +### [OpenListTeam/OpenList-Worker](https://github.com/OpenListTeam/OpenList-Worker) + +[**OpenList Worker**](https://github.com/OpenListTeam/OpenList-Worker) (repo `OpenList-TSWorker`) is the official TypeScript + Serverless port of [OpenList](https://github.com/OpenListTeam/OpenList). The Go backend is rewritten as a TypeScript service running on edge platforms (Cloudflare Workers / EdgeOne Cloud Function / Alibaba Cloud ESA), while the frontend keeps the same interface and interaction as the official OpenList frontend. + +- **Backend**: Hono.js, runs on Cloudflare Workers / EdgeOne Functions / Alibaba Cloud ESA +- **Frontend**: SolidJS + TypeScript, Hope UI, built with Vite +- **Database**: Cloudflare D1 (SQLite), MySQL / MariaDB +- **Cache**: Cloudflare KV / EdgeOne Blob / ESA EdgeKV (optional) +- **License**: AGPL-3.0 + +## Features + +### Storage Aggregation + +Built-in **81 storage drivers** to mount various storage backends out of the box: + +- **Domestic drives**: Aliyundrive (Open Platform / share), Quark (Open Platform / UC TV), Baidu (album), 115 (Open Platform / share), 123 (Open Platform / share), Tianyi Cloud, China Mobile Cloud, Xunlei, Tencent Weiyun, Lanzou, PikPak (share), Doubao, Teambition, WPS, Alidoc, etc. +- **International drives**: Google Drive (album), OneDrive, Dropbox, MEGA, MediaFire, Proton Drive, Yandex Disk, TeraBox, etc. +- **Object storage**: S3-compatible (AWS/OSS/COS/MinIO), UPYUN USS, Azure Blob, WebDAV, FTP, SFTP, SMB, IPFS, etc. +- **Code hosting**: GitHub, GitHub Releases, CNB Releases +- **Drive programs**: OpenList (share), AList V3, Cloudreve V3/V4, Kodbox, Seafile, Teldrive, Febbox, etc. +- **Others**: NetEase Cloud Music, Misskey, Emby, Cloudflare image bed, etc. + +In addition to the real storages above, virtual/functional drivers such as `Local`, `Alias`, `UrlTree`, `AutoIndex`, `Strm`, `Crypt`, `Virtual`, `Chunk` are provided for local mount, address alias, URL lists, encrypted storage and chunking scenarios. + +### Core Capabilities + +- **File browsing**: unified directory tree browsing with online preview for images, videos, audio, documents, code, archives, etc. +- **Upload & download**: cross-storage upload, batch download, streaming transfer and direct-link redirect. +- **File sharing**: generate share links with expiration, password and permission control; support anonymous access and directory sharing. +- **Full-text search**: quickly search files in indexed storages. +- **Offline tasks**: background task queue for batch operations and async processing. +- **External interfaces**: expose aggregated storage via WebDAV or S3-compatible protocol for mounting into third-party tools. +- **MCP service**: provide a Model Context Protocol endpoint that can be integrated and called by AI assistants and other clients. + +### Permission Management + +- **Access control**: role-based access control (RBAC), supporting user groups, directory-level read/write permissions and quotas. +- **Authentication**: built-in account/password, TOTP verification, WebAuthn/FIDO login, SSO single sign-on and LDAP directory authentication. +- **Security hardening**: JWT sessions, CSRF protection, clickjacking protection (X-Frame-Options), Content Security Policy (CSP). +- **Health checks**: `/health` liveness probe and `/healthz` readiness probe for monitoring and alerting. + +### Supported Platforms + +- **Runtime platforms**: Cloudflare Workers, Tencent Cloud EdgeOne Functions, Alibaba Cloud ESA. +- **Data storage**: Cloudflare D1 (SQLite) as primary, with external MySQL / MariaDB support. +- **Persistent cache**: Cloudflare KV / EdgeOne Blob / ESA EdgeKV (optional), used for configuration persistence and caching. +- **One-click deploy**: one-click deploy buttons + initialization for Cloudflare Workers, EdgeOne and Alibaba Cloud ESA. + +## Documentation + +- [Design Architecture](./basic) — Tech stack, data storage backend and supported platforms +- [How to Deploy](./guide) — One-click deploy overview and local development + - [Cloudflare Workers](./guide_cfw) — Step-by-step Cloudflare Workers deployment (with screenshots) + - [EdgeOne](./guide_eom) — Tencent Cloud EdgeOne deployment + - [Alibaba Cloud ESA](./guide_esa) — Alibaba Cloud ESA deployment +- [Environment Variables](./guide_env) — `DB_FORMAT`, `DB_DRIVER` and all runtime variables +- [FAQ](./about) — Common issues and troubleshooting + +## License + +`OpenList` is open-source software licensed under [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.txt). diff --git a/pages/en/faq/_example.md b/pages/en/faq/_example.md new file mode 100644 index 000000000..015122576 --- /dev/null +++ b/pages/en/faq/_example.md @@ -0,0 +1,12 @@ +--- +# title: +# en: FAQ +# zh-CN: FAQ +# categories: +# - faq +# top: 100 +--- + + + + diff --git a/pages/en/faq/error.md b/pages/en/faq/error.md new file mode 100644 index 000000000..fe9b6afc8 --- /dev/null +++ b/pages/en/faq/error.md @@ -0,0 +1,166 @@ +--- +top: 1 +categories: + - faq +--- + +# OpenList Error-Code + +This article will collect some error codes that may occur during the use of OpenList and provide corresponding solutions (including network issues, changes in cloud storage APIs, and other cases that may require manual intervention). + +--- + +**Q**:Ali cloud disk open appears **TooManyRequests** 、**Too Many Requests** + +**A**:[**Click me to view detailed description**](../guide/drivers/aliyundrive_open.md) + +--- + +**Q**:Ali cloud disk open appears **ExceedCapacityForbidden** + +**A**:[**Click me to view detailed description**](../guide/drivers/aliyundrive_open.md#4-other-instructions) + +--- + +**Q**:Token is expired(**Appears when logging in to the OpenList account**) + +**A**:It means that your `OpenList` login is valid for `48` hours by default, you can modify the configuration file `config.json` + +- If you are prompted to log in successfully when you log in and then this prompt is displayed again, check whether you have used CDN acceleration to cache the OpenList. + +--- + +**Q**:Failed init storage but storage is already created: failed init storage: failed to refresh token: The input parameter refresh_token is missing. Please refer to document. + +**A**:Generally, the refresh token (token) is wrong when adding `driver`, and it can be solved by replacing it with the correct one. + +--- + +**Q**:failed get objs: failed to list objs: ForbiddenDriveNotValid:not valid driveld + +**A**:Generally, it means that `driver` has been deprecated. For example, Aliyun disk can be replaced with [**Alibaba cloud disk open**](../guide/drivers/aliyundrive_open.md). Others are temporarily unknown. + +--- + +**Q**:no such host、TLS handshake timeout、read: connection reset by peer、dns lookup failed、connect: connection refused、Client.Timeout exceeded while awaiting headers、network is unreachable + +**A**:These problems are generally caused by network problems, and you can troubleshoot and solve them yourself. + +- If you encounter it when you add `Aliyun disk open`:TLS handshake timeout ([Click me to see how to solve](./why.md#prompt-when-adding-aliyun-drive-shared-prompt-post-https-auth-aliyundrive-com-v2-account-token-net-http-tls-handshake-timeout)) + +--- + +**Q**:Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path (**appears when mounting the driver**) + +**A**:The path to mount to, it is unique and cannot be repeated + +--- + +**Q**:Key: 'Storage.MountPath' Error:Field validation for 'MountPath' failed on the 'required' tag (**appears when mounting the driver**) + +**A**:The mount path is a required option, please fill in it + +--- + +**Q**:UNIQUE constraint failed: x_meta.path (appears when adding meta information) + +**A**:When adding meta information, there can only be one path, and it cannot be repeated + +--- + +**Q**:Key: 'Meta.Path' Error:Field validation for 'Path' failed on the 'required' tag (appears when adding meta information) + +**A**:When adding metadata, the path must be filled in + +--- + +**Q**:failed get objs: failed to list objs: Sorry, sharing is not available in the current region(**PikPak/share**) + +**Q**:failed get objs: failed to list objs: terabox is not yet available in this are(**Terabox**) + +**A**:Domestic access is not supported, if you build it locally, you can check this [**Reference Solution**](https://anwen-anyi.github.io/index/07-wenti.html#_41-alist如何-使用-吃到-代理-proxy) + +- For example, Google, Mega, Terabox, etc. that require a proxy to access can be used in this way + +--- + +**Q**:Search not available(**appears when indexing**) + +**A**:The `Search Index` option is not selected, and cannot be built and used. I don’t know which search index to choose? [**Click me to view**](../guide/advanced/search.md#difference-between-different-search-indexes) + +--- + +**Q**:only chinese and english, numbers and underscores are supported, and the length is no more than 50 (**Appears when the baidu.photo folder is renamed**) + +**A**:When renaming the baidu.photo folder, the maximum length is 50 + +--- + +**Q**:failed get objs: failed to list objs: NotFound.FileId:The resource file_id cannot be found. file_id:634e704cefa78f92fefd4c779f7422d820082d041(**Add Alibaba cloud disk open**) + +**A**:When adding the open storage of Alibaba Cloud disk, `root folder ID` is wrong, which of the last ID above is the wrong ID, just get the correct replacement. + +--- + +**Q**:System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat + +**A**:Your Tampermonkey answering plug-in conflicts, just close it [**For details, click to view**](https://github.com/alist-org/alist/discussions/2399) + +--- + +**Q**:Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. + +**A**:If you enter the wrong password for 6 consecutive logins, it will be locked, and you can reset it by restarting OpenList. + +--- + +**Q**:Failed get storage: please add a storage first. (**When adding offline download files**) + +**A**:When adding an offline download file, you need to enter which cloud disk you want to download the offline download file to and then click on the `folder` instead of adding it on the home page [**Complete Instructions**](../guide/advanced/offline-download.md) + +--- + +**Q**:failed get objs: failed to list objs: Unable to retrieve user's mysite URL(**When adding onedrive_app**) + +**A**:The newly created `OneDrive` user account does not take effect in real time, Delay takes effect, wait for a few hours and try again [**Case**](https://github.com/alist-org/docs/discussions/189#discussioncomment-5928892) + +--- + +**Q**:failed to start: listen tcp 0.0.0.0:5244: bind: address already in use (**When starting the OpenList program**) + +**A**:Port number 5244 is already in use, check whether it is occupied (generally you have started an OpenList with port 5244), or modify the port number started by OpenList, [**How to modify**](../configuration/configuration.md#scheme) + +--- + +**Q**:**[When OpenList upload file](why.md#why-do-i-get-413-http-code-when-i-upload-a-file)**:Request failed with status code 413 + +**A**:Limit the size of the files configured nginx, modify the nginx's `client_max_body_size`,If you are a pagoda to go to the pagoda page to modify [Example](https://blog.csdn.net/u012514495/article/details/127981183) + +--- + +**Q**:**failed get objs: failed to list objs: query fail [61008]** + +**A**:It may be because some drivers do not support modifying the file sorting. Try to cancel the file sorting. + +--- + +**Q**:When running `docker logs openlist`, the error appears: FATA[2025-08-12 02:48:46] failed to create config file: open /opt/openlist/data/config.json: permission denied 。 + +**A**:This is caused by the directory mounted not matching the user permissions running the docker. The solution: + +1. Use the `--user` parameter to specify the user running the container, assuming the user is `1000:1000`. + + First, ensure that the `${yourDataDir}` directory has permissions of `1000:1000`. If not, use `sudo chown -R 1000:1000 ${yourDataDir}` to change the directory permissions. + + Then run the Docker with the following command: + +```bash +docker run -d --name openlist --user 1000:1000 -v ${yourDataDir}:/opt/openlist/data -p 5244:5244 openlistteam/openlist +``` + +2. Change the owner of the `${yourDataDir}` directory to `1001:1001`, and then run the Docker: + +```bash +sudo chown -R 1001:1001 ${yourDataDir} +docker run -d --name openlist -v ${yourDataDir}:/opt/openlist/data -p 5244:5244 openlistteam/openlist +``` diff --git a/pages/en/faq/howto.md b/pages/en/faq/howto.md new file mode 100644 index 000000000..9e159da09 --- /dev/null +++ b/pages/en/faq/howto.md @@ -0,0 +1,113 @@ +--- +top: 1 +categories: + - faq +--- + +# How to + +## How to add password for a file/folder? + +Add a [meta](../guide/advanced/meta.md) record. + +## How to reverse proxy with sub directory? + +An example of using nginx to reverse proxy to https://example.com/openlist: + +- Normal installation +- Set [site_url](../configuration/configuration.md#site-url) to `https://example.com/openlist` or just `/openlist` then restart openlist +- Add a reverse proxy record in nginx + +```nginx +location /openlist/ { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Range $http_range; + proxy_set_header If-Range $http_if_range; + proxy_redirect off; + proxy_pass http://127.0.0.1:5244/openlist/; + # the max size of file to upload + client_max_body_size 20000m; +} +``` + +## How to get password if i forget it? + +If the password is forgotten, it can only be re-**`randomly generated`** or **`manually set`** + +```bash +# Randomly generate a password +./openlist admin random +# Manually set a password, `NEW_PASSWORD` refers to the password you need to set +./openlist admin set NEW_PASSWORD +``` + +## How to modify the listening port + +Refer to [config](../configuration/configuration.md#scheme) + +## How to upgrade + +Except for the incompatible version marked in the changelog, you can directly replace the binary file to upgrade. + +For Docker user, just remove the old container and pull the new image then run it. + +## How to allow guest to upload files + +Add a [meta](../guide/advanced/meta.md) record, and open `write` field. + +## How to remove "Powered by OpenList" at the bottom? + +According to our open source license: +Permissions of this strongest copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. **Copyright and license notices must be preserved.** Contributors provide an express grant of patent rights. When a modified version is used to provide a service over a network, the complete source code of the modified version must be made available. + +## When adding a 189 Cloud storage: the device ID does not exist, and a secondary device verification is required + +Open the Tianyi Account website at , log in, and then disable the Device Lock.. + +## When adding 189 Cloud PC storage: prompt need img validate code: verification code + +- Click Edit, write the verification code you just saw into the configuration and click Save +- Click Edit and turn on the Do not use OCR button +- Or build it yourself [**Ocr interface**](../configuration/global.md#ocr-api) +- **189 Cloud** Driver has been replaced with sliding verification code because web login has been replaced.**No longer supports OCR and manual input**, if the verification code needs to be used, please use the addition of `Cookie to log in` or use the `189 Cloud PC` Driver. Note: The 189 Cloud Driver is different from the 189 Cloud PC Driver. + +## TLS handshake timeout? / read: connection reset by peer? / dns lookup failed? / connect: connection refused / Client.Timeout exceeded while awaiting headers? / no such host? + +For network problems such as these, please troubleshoot and solve them yourself.Don't create any issues for this. + +## How to add epub reading + +Background --> Settings --> Preview --> Iframe preview, written behind the PDF + +```html{2-5} + /*The comma below is also oh, don’t copy this comment, start copying from the second line*/ +, + "epub": { + "EPUB.js":"/static/epub.js/viewer.html?url=$e_url" + } +``` + +Version 3.7.x and higher already support ".epub" reading +But you need to add it manually (because the database has already been created, it is not good to overwrite it for you, and you will make mistakes) +If it is the first installation and startup (version 3.7.x and higher), no need to add it manually +If the secondary directory reverse generation is set, please add the corresponding prefix in [site_url](../configuration/configuration.md#site-url), and then restart OpenList to take effect + +### How to quickly locate bugs + +If you find a bug, but the `log.log` log is not detailed, you can try to add the `--debug` parameter to start + +It is recommended to clear the log file `/log/log.log` in the **OpenList directory before starting with the `--debug` parameter**, so that developers can quickly locate problems later + +::: danger +When using the `--debug` parameter to start, there will be some sensitive data such as **`account password, refresh token`**, etc., so if you need to desensitize before sending it to others +::: + +- **Windows**:`openlist.exe server --debug` +- **Linux**:`./openlist server --debug` +- **Mac**: `./openlist server --debug` +- **Docker**:`docker exec -it openlist ./openlist server --debug` (to be modified, because this command runs OpenList in an already started container, so it will not start a second OpenList) + +How to stop the relevant logs after startup? `Ctrl+C` can stop the program (or simply close the program directly) diff --git a/pages/en/faq/index.md b/pages/en/faq/index.md new file mode 100644 index 000000000..2e7933f58 --- /dev/null +++ b/pages/en/faq/index.md @@ -0,0 +1,3 @@ +# FAQ + + diff --git a/pages/en/faq/seealso.md b/pages/en/faq/seealso.md new file mode 100644 index 000000000..df3a2d464 --- /dev/null +++ b/pages/en/faq/seealso.md @@ -0,0 +1,21 @@ +--- +top: 1 +categories: + - faq +--- + +# See also + +### **The following resources may help you** + +> Third party tutorials and information may be outdated and incorrect, and no warranty of availability is made here. + +[https://space.bilibili.com/36411485/channel/collectiondetail?sid=686340](https://space.bilibili.com/36411485/channel/collectiondetail?sid=686340) + +[https://anwen-anyi.github.io/](https://anwen-anyi.github.io/index/) + +[Google](https://www.google.com/search?q=openlist) + +[Bing](https://www.bing.com/search?q=openlist) + +[Baidu](https://www.baidu.com/baidu?wd=openlist) diff --git a/pages/en/faq/why.md b/pages/en/faq/why.md new file mode 100644 index 000000000..412dc3d9e --- /dev/null +++ b/pages/en/faq/why.md @@ -0,0 +1,305 @@ +--- +top: 1 +categories: + - faq +--- + +# Why + +## Why I got the error `failed get storage: can't find storage with rawPath: /` when I open web pages after install/upgrade? + +Because you haven't added storage. Click the `Login` button at the bottom to log in and then click `Manage` button to enter the background and add storage on the storage page. + +## why i lost all my storage in v3 + +New versions (V3 and later) are not compatible with V2, so you need to re-add your storage. + +## Why I see `failed get aria2 version` error in log? + +Because you haven't installed aria2 or the settings of aria2 are wrong. Don't worry, it won't affect the use of OpenList, you can ignore it. + +## why i got the error `Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path` when I add new storage? + +I guess the error message is pretty clear. Because [mount_path](../guide/drivers/common.md#mount-path) is **UNIQUE**. +If you see this error when adding storage, but you don't see the corresponding duplicate entry on the web page, use `Ctrl` + `F5` to force refresh page. + +## Why can't I preview the video or audio file? + +- Requires a browser-supported encoding +- General browsers do not support h265 encoded video +- General browsers do not support ac3 encoded audio + +In particular, for Safari: + +- For cross-origin media, content-type must be the media type, not the generic application/octet-stream. So if the content-type returned by the direct link is application/octet-stream, then Safari will not be able to play.(PS: Why Can AliyunDrive play in Safari? Because the official website of the video playback is not the use of download links, but after the transcoding of AliyunDrive) +- Aliyun disk development platform driver supports online video playback (also limited to Aliyun disk open platform driver and **Aliyun Video Previewer** option player) + +## Why can't I preview the office file? + +For microsoft office online viewers, you need: + +- External network access +- The document access address cannot use ip directly, it needs to be accessed through a domain name, and the port must be port 80 / 443 +- The format of the document (must be one of the following): +- Word: docx, docm, dotm, dotx +- Excel: xlsx, xlsb, xls, xlsm +- PowerPoint: pptx, ppsx, ppt, pps, pptm, potm, ppam, potx, ppsm +- Document size: Word and PowerPoint documents must be less than 10 megabytes; Excel must be less than five megabytes + +## Why can't I preview the PDF file? + +- We use `pdf.js` as the default PDF previewer. So it requires https+cors. +- https is opened for yourself, reverse proxy or provide a certificate in the configuration file +- cors provides support for related cloud disks and cannot be modified by yourself unless the agent is turned on to use the program to transfer + +## Why can't package download? + +Same as pdf preview, requires https+cors support,and: + +- OpenList use stream API of browser to support package download, so it requires browser support. Usually, it is supported by new browsers, such as Chrome, Firefox, Edge, etc. + +## Why does it shows `Uploading in the backend` when I upload a file? + +- All uploads of this program use server transfer, which will consume server traffic to communicate with the corresponding storage API + +## Why do i get `413` http code when i upload a file? + +- If you use a reverse proxy, you may need to specify the maximum upload file size(`client_max_body_size`) and timeout in the web server configuration +- Otherwise, it may happen that the upload is successful but the front end times out and there is no response + +## Why can't I download files after add `AliyunDirve` storage and get `InvalidArgument` error? + +::: warning +This is an outdated driver that has been abandoned, is no longer maintained, and will be removed in future versions. We recommend using the officially supported Aliyundrive Open. +[**Click to view this guide**](../guide/drivers/aliyundrive_open.md) +::: + +Due to referrer restrictions, mobile tokens must be used if you don't open any proxy. + +## Why is the uploaded file not displayed/deleted file still/modified in the root directory does not take effect? + +There is a half-hour cache by default, which can be modified on the Add Storage page. If you need to refresh immediately, click the refresh button in the lower right corner of the directory that needs to be refreshed. + +## Why is it still incorrect although I input the password that get by `./openlist admin` in terminal? + +Check whether the directory you run `./openlist admin` and start OpenList is the same. OpenList reads the configuration in the current directory where you run the program by default, so running in different directorys will result in different data being read. + +--- + +- **Here take Windows as an example**: For example, your OpenList file is in the **`D:\Test\OpenList\`** folder, and then you directly start the CMD command running window on the desktop, and then put the D drive Drag the OpenList inside to the command running window to start, and then the configuration file it generates is actually in **`C:\Windows\System32`** here (because CMD is here by default), and then you Obtaining the password is also obtaining the password in this folder. At the same time, because OpenList reads the password in the configuration file in the same directory as OpenList, it is naturally wrong for you to log in with the password. + - ![faq](/img/faq/alist-admin.png) +- Solution: You need to go to the folder where OpenList is located to start or check the password, for example, the above is in **`D:\Test\OpenList\`**, you have to manually go to this folder and then enter Enter the startup command or the command to view the password to obtain the correct password + - **Windows**: After going to the directory where OpenList is located, enter CMD in the top address bar and press Enter, and then use the command to obtain the password + - ![faq](/img/faq/alist-admin.gif) + - **Linux**: Use the CD command to go to the directory where OpenList is located to get the correct password + +## Why does it prompt System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat + +This is caused by the conflict between some scripts in the Tampermonkey extension and the webpage code, such as some automatic answering scripts. +Please check and disable the relevant scripts on your own. + +## What is the difference between the two uploads? + +1. **stream** directly puts the binary content of the file into the body, so that the backend can use very little memory, it seem to have no downsides. + +2. **form** is to wrap the file into formdata and upload it. The memory used by the backend will be more, but there is no limit on the size. + +## What is the difference between the two Aria2? + +1. Background management --> Settings --> Others: **Aria2 here is used to download resources offline to the network disk** + +2. Front-end interface --> Local settings (the gear in the lower right corner) --> **Aria2 here is used to download the resources in the network disk to the local, which is equivalent to a download method, everyone can use it without worrying about problems** + +- As for how to use the first offline download: + - For example, if you want to download some resources offline in the **`/OneDrive/TV/Test`** folder, you must first fill in the **address and secret key information** in the background and then manually enter the front end In this folder of the interface, you can see the **offline download** icon (a magnet style) in the lower right corner after entering it, click and enter the link you want to download offline + - `Description`: **Offline download, copy and upload** are all in the same way. They are first downloaded from the resource **to the temporary folder of the server** and then uploaded from the **server to the corresponding network disk** + - "Copy" can be directly transferred by the server without temporary storage for uploads that **partially do not require** to calculate the Hash value. + +```mermaid +flowchart LR + A([ready]) --> B[Download to a temporary folder] + B --> C[The server uploads] + C --> D[Upload to the network disk] + D --> E([end]) +``` + +## RaiDrive mounts Baidu network disk, you can download files, but you cannot upload files. Throwing files to the mount directory prompts "Prompt "You need permission to perform this operation". How to solve this? + +Raidrive is because uploading a file will create an empty file in advance, and Baidu Netdisk does not allow the creation of an empty file. + +## Why is the Terabox mounted successfully but the content is not displayed? + +Because **`Terabox`** restricts IP, if you access it from a non-overseas IP, you cannot access it, and naturally the content will not be displayed. + +(Overseas machines can be used. If you have to use local machines (or domestic machines) to build, you can find a way to let OpenList eat **proxy**) + +**Note: Check the relevant settings to ensure that traffic related to '\*.terabox.com' goes through the **proxy\***\* + +## Open OpenList prompt System error: TypeError: n.replaceAll is not a function? + +Your browser kernel version is too low, just add the following to the custom head. + +```html + + +``` + +If you think it is too slow, you can change to Ali Cloud + +```html + +``` + +## Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. + +![faq](/img/faq/error.png) + +If you enter the wrong password for 6 consecutive logins, it will be locked, and you can reset it by restarting OpenList. + +## When adding a file to upload, there is an option of "add as task", what does it do? + +- After checking, upload to the server (the machine where OpenList is built), and then when uploaded by the server to the network disk, it seems to be visible in the background (it seems to be like this) +- You will not see it if it is not checked. For details, you can see the problem in the picture below. + +![faq](/img/faq/add.jpeg) + +## I am asked to enter the OTP code when I log in. What is the reason? + +Are you logging into someone else's account? If it is your own, please enter the 2FA (two-step verification) verification code + +What? Don't know the verification code? [**Click to view this guide**](../guide/advanced/2fa.md) + +## Use **`sqlite3`** to find that there are more than two files in the data folder, data.db-shm and data.db-wal + +These are temporary files created by SQLite in WAL mode to support database transaction processing and concurrent access. The .db-shm file is a shared memory file used to store temporary data, while the .db-wal file is a write-ahead log file that records transaction changes for rollback in case of transaction failure. These two files are associated with the .db file, and it is not recommended to delete them arbitrarily. + +## **How do I set it so that visitors can only see the content after logging in?** + +There is always one of the following three versions that suits you. Eat it quickly! ! + +1. Point the directory seen by **`guest`** users to an empty folder, and then write a readme description, don’t ask how to do it yourself[Click to view](#how-to-create-a-completely-blank-folder) + +2. Directly encrypt the root directory in the meta information + +3. Directly deactivate the **`guest`** user, which will force a jump to the login page (version 3.10.1 and above) - Find a way by yourself Welcome to provide new ways~~ + +## Baidu cloud disk unofficial download error appears "hit black userlist, hit illegal dlna" + +It should be repaired harmoniously, but the interface discoverer said to wait for 24 hours to try, if it still doesn't work, it is really repaired harmoniously. + +```json +{ + "error_code": 31329, + "error_msg": "hit black userlist , hit illegal dlna", + "request_id": 8942439159334999112 +} +``` + +## How to create a completely blank folder + +two ways + +1. Mount a local storage, select the folder is empty +2. Select virtual storage and directly change **"number of files" "number of folders"** to 0, save and then ignore the pop-up interface + - What does **Virtual Storage** do? It is generally used during testing. Don’t use it indiscriminately if you don’t understand the normal production environment.... + +How to add **`Readme`** instructions to an empty folder + +1. If you choose local storage, you can consider throwing a Readme.md file into it. If there is an update but the refresh is slow, use the second method if it is too slow +2. Meta information Select this folder, there are instructions below to write it yourself + +What is the use of creating this blank folder? It can be used in conjunction with [**How to set up visitors to access the content after logging in, use the first method**](#how-do-i-set-it-so-that-visitors-can-only-see-the-content-after-logging-in) + +## lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./openlist) or accept: function not implemented + +Consider using the **musl** version as an alternative due to the version of the **glibc** on your platform is too low,or use docker version. + +## What is the extra sign parameter after the file now? + +Encrypted for account security, you can view the details by [click to view](../configuration/global.md#sign-all) + +## Prompt when adding Aliyun drive (shared) Prompt Post "https://auth.aliyundrive.com/v2/account/token": net/http: TLS handshake timeout + +Reason: The VPS is located outside mainland China, and IPv6 is enabled +Workaround: +**Method 1**: Add the following three items in the vps hosts and restart the network or restart the vps + +- 47.94.245.197 api.aliyundrive.com + +- 47.94.245.197 auth.aliyundrive.com + +- 121.89.3.30 cn-beijing-data.aliyundrive.net + +**Method 2**: Turn off and disable IPv6 of vps + +## 123 When the cloud disk is mounted, json: cannot unmarshal string into Go struct field TokenResp.data of type struct { Token string "json:\"token\"" } + +![faq](/img/faq/123error.png) + +There is a high probability that your device that builds OpenList is temporarily blocked, frozen, blocked, or restricted. Don't mount it yet, wait a few hours and try again or wait a day + +(Conclusions from the above questions have been tested) + +## Why doesn't PikPak have a name + +Because PikPak's server does not set the filename in the `content-disposition` header, this is a PikPak problem and not an OpenList, so it cannot be fixed + +## How to query your own OpenList version number + +1. Use the command `openlist version`, the console will output +2. OpenList Manage --> Settings --> Site --> `Version` option + +Why is the old version of OpenList still after the update? Generally, docker encounters this problem. You can [**refer to this**](../guide/installation/docker.md#update) + +## OpenList's download "Download" option suddenly blank, no content + +Check your plug-ins, whether there are ad blocking plug-ins, for example: **AdGuard** and others + +Through the console, it is found that "**https://g.alicdn.com/IMM/office-js/1.1.5/aliyun-web-office-sdk.min.js**" has been blocked, + +Maybe other links are blocked, manually add the blocked domain name to the whitelist or close the plug-in, or it may be that the plug-in is convulsed + +![faq](/img/faq/download.png) + +## Why do prompts appear after clicking "Login success" and "Guest is disabled, login please" + +Please check whether your site is connected to a `CDN`. If so, please remove the CDN cache. + +Connecting OpenList to a CDN is not recommended for non-professionals. [See here](https://t.me/OpenListOfficial/23). + +## Why has the "Add" button on the sharing management page been disabled? + +Your account does not have "Sharing" permissions. + +- If you are an administrator, please go to the User Management interface to enable the required permissions for yourself. +- If you are a regular user, please contact your administrator to request access. + +## Why can't I use a WebDAV client to manage (writing operations) files in OpenList? + +The reason is the same as the previous one: you need the "WebDAV Manage" permission. + +## Why is my Markdown file preview showing garbled text? + +## Why aren't lists in my Markdown file visible during preview? + +This phenomenon is mainly observed in Markdown files that include tables or code. As a security measure to prevent the execution of potentially malicious scripts from untrusted sources, the system has XSS protection enabled by default. + +This mechanism can be disabled by navigating to "**Settings / Preview**" and deactivating the "**Filter scripts in README**" option. + +Please note that disabling this protective measure will make your device vulnerable to XSS (cross-site scripting) attacks. Users are fully responsible for the security of their own devices. + +## Why can't I connect to OpenList via an FTP client even after I've filled in the FTP mount configuration? + +Besides configuring the settings in the admin panel, you must also set `enable` to `true` under the `ftp` section in the configuration file (`config.json`) to activate the FTP connection feature. + +Additionally, any changes made in the admin panel require a restart of OpenList to take effect. + +If you wish to connect using the SFTP protocol, please enable it by setting `enable` to `true` in the `sftp`(rather than `ftp`) section of the configuration file. + +## Why are the filenames in my ZIP file showing up as garbled text/characters when I preview it? + +Older versions of the ZIP format do not mandate UTF-8 encoding for filenames, which can lead to decoding problems for non-ASCII characters. + +To resolve this, you can adjust the "**Alternative Encoding for ZIP Files**" setting under "**Settings / Preview**". + +For Simplified Chinese users, you can fix this issue by setting this value to "**GBK**" in most cases. diff --git a/pages/en/guide/advanced/2fa.md b/pages/en/guide/advanced/2fa.md new file mode 100644 index 000000000..41bb64e90 --- /dev/null +++ b/pages/en/guide/advanced/2fa.md @@ -0,0 +1,51 @@ +--- +categories: + - guide + - advanced +top: 110 +--- + +# 2Fa + +## Enable 2FA verification + +To enable 2-factor authentication, you need to install a app that support 2FA (such as [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2),[Microsoft Authenticator](https://support.microsoft.com/en-us/account-billing/download-and-install-the-microsoft-authenticator-app-351498fc-850a-45da-b7b6-27e523b8702a)), on your phone. + +Then login to OpenList manage and goto `Profile` page, click `Enable 2FA` button, scan the QR code with your 2FA app, and enter the code generated by your 2FA app. + +Finally, click `Verify` button to enable 2FA. + +- Each user can set a different "2FA" verification, how to cancel see the method below + +## Cancel 2FA verification + +**1. Forgot 2FA for a non-Admin account:** + +If you’ve forgotten the 2FA for a non-admin account, you can contact an administrator to clear it. The steps are as follows: + +- Go to the backend → Select "Users" → Click **"Cancel Two-Factor Authentication"**. + +If the 2FA is lost for an admin account, please refer to step 2 for instructions on clearing it. + +--- + +**2. How to access the folder where OpenList is located and clear 2FA:** + +- **Windows**: Navigate to the folder where OpenList is located and enter the command: + `openlist.exe cancel2fa` + +- **Linux**: Similarly, go to the OpenList folder and enter the command: + `./openlist cancel2fa` + +- **Docker**: In a Docker environment, enter the command: + `docker exec -it openlist ./openlist cancel2fa` + +After running these commands, a restart will apply the changes. + +--- + +**3. Actions after canceling 2FA:** + +In the "Users" section of the backend, you will find the **"Cancel Two-Factor Authentication"** option. After canceling, if issues persist, try restarting OpenList. + +**Note**: If you don’t see the **"Cancel Two-Factor Authentication"** option, please check if you’re using the latest version. If not, please update your version. diff --git a/pages/en/guide/advanced/alias.md b/pages/en/guide/advanced/alias.md new file mode 100644 index 000000000..c04dc1014 --- /dev/null +++ b/pages/en/guide/advanced/alias.md @@ -0,0 +1,10 @@ +--- +categories: + - guide + - advanced +top: 120 +--- + +# Alias + + diff --git a/pages/en/guide/advanced/backup.md b/pages/en/guide/advanced/backup.md new file mode 100644 index 000000000..eaaf79819 --- /dev/null +++ b/pages/en/guide/advanced/backup.md @@ -0,0 +1,32 @@ +--- +categories: + - guide + - advanced +top: 20 +--- + +# Backup&Restore + +## Method 1-Built-in + +Use `OpenList` to manage `Backup/Restore` options + +1. Backup: Backup OpenList data (**does not include index**) +2. Recovery: Backup files before recovery +3. Override: Select override when recovery, and will override the user information +4. Encryption Password:Fill in the `ENCRYPTION PASSWORD` Export, the information will be encrypted when exporting.You also need to enter the `ENCRYPTION PASSWORD` first before importing. + + ![](/img/advanced/backup.png) + +## Method 2-Sqlite3 + +Back up the `data/data.db` database in the `OpenList` directory, and wait for the new environment to replace it directly (**contains index data**) + +- If there are two files, if there are `data.db-shm` and `data.db-wal`, they must be backup together. +- It is recommended to stop running OpenList and then backup, so that the two files of `data.db-shm` and`data.db-wal` will be merged with the `data.db` + + ![](/img/advanced/sqlite3.png) + +## Method 3-Other database + +Other, if you use `mysql, postgres` and other non `sqlite database`, please solve it by yourself (**include indexing data**) diff --git a/pages/en/guide/advanced/balance.md b/pages/en/guide/advanced/balance.md new file mode 100644 index 000000000..7057627c5 --- /dev/null +++ b/pages/en/guide/advanced/balance.md @@ -0,0 +1,104 @@ +--- +categories: + - guide + - advanced +top: 90 +--- + +# Load balancing + +## Link load balancing + +Link load balancing distributes traffic across multiple links to alleviate bandwidth pressure on any single link. This load balancing mechanism is only applicable when traffic arrives at the same storage service via multiple distinct links. The system assumes that the drivers participating in the load balancing can always automatically maintain content consistency and will simply forward all requests in a round-robin manner: + +```mermaid +graph LR + User -->|RW file1| OpenList + OpenList -->|Write file1| Link1 + OpenList -->|Read file1| Link2 + Link1 -->|Write file1| Serv[Storage Service] + Link2 -->|Read file1| Serv[Storage Service] +``` + +To perform this load balancing mechanism, mount the first driver normally, and then add the remaining load-balancing drivers using a mount path formatted as `first storage mount path + .balance + any additional content`. + +E.g: + +- Storage 1: `test` +- Storage 2: `test.balance1` +- Storage 3: `test.balance2` +- Storage 4: `test.balance3` +- ... +- Storage n: `test.balancen` + +![](/img/advanced/balance.png) +The first is marked with a red box. It is the main mount, which is displayed on the front page. The remaining nine are the first load balancing on the first one. + +## Storage load balancing + +Storage Load Balancing is used to distribute storage occupancy across multiple storage services, achieving the effect of abstracting multiple storage spaces into one large storage space with a capacity equal to the sum of all individual spaces (similar to RAID 0). The system will assign each uploaded file to a random storage service and forward all read and modification requests for that file to that specific service: + +```mermaid +graph LR + User -->|Requests| OpenList + OpenList -->|Write file1| Serv1[Storage Service 1] + OpenList -->|Write file2| Serv2[Storage Service 2] + OpenList -->|Read file1| Serv1 + OpenList -->|Rename file1| Serv1 + OpenList -->|Read file2| Serv2 +``` + +This type of load balancing can be implemented using the putting load balancing feature of the [Alias](/en/guide/drivers/alias) driver, with the following configuration: + +- **Reading conflict policy**: **Get the file corresponding to the first conflict path** (The load balancing function of the Rading conflict policy is used to implement the load balancing described in the [Multi-source reading load balancing](/en/guide/advanced/balance#multi-source-reading-load-balancing) section. Based on the principles of the two load balancing schemes, enabling both simultaneously does not achieve a 1+1>=2 effect). +- **Writing conflict policy**: Choose either **Allow full conflict paths** or **Write into all conflict paths** (If the **Write into the first conflict path** policy is used, folder creation operations will only be forwarded to one driver. This will cause that folder and its descendant folders to be unable to continue load balancing as they won't exist on other drivers). +- **Putting conflict policy**: Choose one of **Random load balancing**, **Weighted random load balancing based on remaining space**, or **Strict weighted random load balancing based on remaining space** according to your needs. + +The effect achieved with the above configuration: + +```mermaid +graph LR + OpenList -->|File1,File2,File3| Alias + Alias -->|File3| Driver1[Driver 1] + Alias -->|File1| Driver2[Driver 2] + Alias -->|File2| Driver3[Driver 3] +``` + +#### Load balancing by file chunks + +Sometimes, the files that need to be load-balanced across storage are relatively large, and performing load balancing on a per-file basis is not granular enough. You can use the [Chunk](/en/guide/drivers/chunk) driver to split files into fixed-size blocks and then apply storage load balancing to these blocks. The specific configuration is as follows: + +- **Chunk**: In the remote path, fill in the mount path of the **Alias** driver. Configure other settings as needed. +- **Alias**: Keep the configuration consistent with the description above. + +The achieved effect is: + +```mermaid +graph LR + OpenList -->|A Large File| Chunk + Chunk -->|Block1, Block2, Block3| Alias + Alias -->|Block1| Driver1[Driver 1] + Alias -->|Block3| Driver2[Driver 2] + Alias -->|Block2| Driver3[Driver 3] +``` + +## Multi-source reading load balancing + +Multi-source reading load balancing allows copies of a file to be distributed across several different storage services. When a user accesses the file, the system randomly selects one of the copies to return, thereby reducing the uplink bandwidth pressure on any single storage service (similar to RAID 1). The key difference between this load balancing strategy and [Link load balancing](/en/guide/advanced/balance#link-load-balancing) is that since the load-balanced storage services are treated as multiple, independent file systems with no automatic synchronization, OpenList will forward write operations to **all** storage services, rather than to just one of them: + +```mermaid +graph LR + User -->|RW file1,file2| OpenList + OpenList -->|Write file1| Serv1[Storage Service 1] + OpenList -->|Write file2| Serv1 + OpenList -->|Write file1| Serv2[Storage Service 2] + OpenList -->|Write file2| Serv2 + OpenList -->|Read file1| Serv1 + OpenList -->|Read file2| Serv2 +``` + +This type of load balancing can be implemented using the reading load balancing feature of the [Alias](/en/guide/drivers/alias) driver, with the following configuration: + +- **Reading conflict policy**: Choose either **Load balancing on a per-file basis** or **Load balancing on a per-part basis**. +- **Writing conflict policy**: Choose either **Allow full conflict paths** or **Write into all conflict paths** (If the **Write into the first conflict path** policy is used, folder creation operations will be forwarded to only one driver. This will prevent that folder and its descendant folders from continuing to participate in load balancing, as they won't exist on other drivers). +- **Putting conflict policy**: Choose either **Allow full conflict paths** or **Put into all conflict paths** (The load balancing function of the Putting conflict policy is used to implement the load balancing described in the [Storage Load Balancing](/en/guide/advanced/balance#storage-load-balancing) section. Based on the principles of the two load balancing schemes, enabling both simultaneously does not achieve a 1+1>=2 effect). diff --git a/pages/en/guide/advanced/ftp.md b/pages/en/guide/advanced/ftp.md new file mode 100644 index 000000000..c38346df9 --- /dev/null +++ b/pages/en/guide/advanced/ftp.md @@ -0,0 +1,163 @@ +--- +categories: + - guide + - advanced +top: 30 +--- + +# FTP / SFTP + +::: tip +Any adjustments made to FTP-related configurations on the web management page must restart OpenList to take effect. + +When using FTP for downloading, only the local proxy will be used. + +::: + +## FTP Configurations + +| Field | Meaning | Example Value | +| --------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | +| enable | Whether to enable | `true` / `false` | +| listen | (Allowed access IP mask): port | `":5221"` (default) / `"0.0.0.0:21"` / `"127.0.0.1:2121"` | +| find_pasv_port_attempts | Maximum attempts to find a port due to port conflict in passive transfer | `50` | +| active_transfer_port_non_20 | Enable ports other than 20 for active transfer ports | `true` / `false` | +| idle_timeout | Maximum idle time (in seconds) without client requests | `900` | +| connection_timeout | Connection timeout time | `30` | +| disable_active_mode | Disable active transfer mode | `true` / `false` | +| default_transfer_binary | Default transfer in binary mode | `true` / `false` | +| enable_active_conn_ip_check | Check the IP of the client for data stream TCP connection in active transfer mode | `true` / `false` | +| enable_pasv_conn_ip_check | Check the IP of the client for data stream TCP connection in passive transfer mode | `true` / `false` | + +## FTP Settings + +Before understanding the FTP configuration options, it is important to first understand how the FTP protocol works. The FTP protocol uses **two TCP connections** for communication, which are referred to as the "**control flow**" and the "**data flow**." Port 21 is the default control flow port for the FTP protocol. The FTP server continuously listens on this port, waiting for connections from clients and responding accordingly. **The control flow only transmits client requests and server error messages, without transferring file contents or file listings.** In OpenList, the control flow port is determined by the `listen` parameter in the configuration file, with a default value of 5221. The client must be able to access this port on the server for the protocol to function properly. + +The establishment of the data flow can be done in two main ways, known as "active mode" and "passive mode": + +In active mode, the client actively listens on a port and sends the port number to the server using the `PORT` command. The server then actively connects to the client and transmits files or a list of files. In this mode, the client must be directly accessible by the server. Therefore, in the context of widespread NAT, this mode generally only works when both the server and client are in the same subnet. + +```mermaid +--- +title: Active Mode +--- +sequenceDiagram + Client->>Server: Sends PORT command with client's IP address and data port number + Server->>Client: Actively connects to the client and transmits files or file list over the newly established connection (data stream) +``` + +In passive mode, the client first sends the `PASV` or `EPSV` command in the control stream, requesting the server to listen on a new data port. The server then returns the port number of the new listening port to the client in the control stream. After the client establishes a connection with that port, data transfer begins. In this mode, the server does not actively initiate connections to the client, so it only needs to be outside of NAT. However, since the passive transfer port is not predetermined but determined before the connection is initiated, additional configuration is required when there are port mappings between the server and the client, or when only a subset of the server's ports are available for client connections in complex network environments. + +```mermaid +--- +title: Passive Mode +--- +sequenceDiagram + Client->>Server: Sends PASV or EPSV command in control stream + Server-->>Client: Server's public IP (only PASV) and passive transfer port number for data transfer + Client->>Server: Initiates TCP connection to the server's IP and port, then transfers files or file list over the newly established connection (data stream) +``` + +- FTP Server Public Network Address + + This is the IP address that the server sends to the client in the `PASV` command. If the server and the client are within the same subnet, the server's internal IP address can be used. Even if the server and the client are on the same machine, `127.0.0.1` can be used. However, if the server and client are not in the same subnet, the server's IP address that is accessible by the client must be specified. + + A domain name can also be specified. In this case, the default DNS will resolve the domain name to an IP address. However, since `PASV` only supports IPv4 addresses, if both an AAAA (IPv6) and an A (IPv4) record exist for the domain, the A record will be used. If only an AAAA record exists without an A record, the result is unknown. + + This field does not affect the `EPSV` command, but leaving this field invalid will cause the FTP server to fail to start. Therefore, if your FTP client only uses the `EPSV` command, you may consider keeping the default value `127.0.0.1`. + +- FTP Passive Transfer Port Mapping + + This field consists of a series of "mapping groups" separated by commas (`,`) or newlines. The legal forms for a "mapping group" are as follows: + 1. `` + 2. `-` + 3. `:` + 4. `-::` + + All port numbers must be between 1024 and 65535 (inclusive), and the starting port number of a range must be less than the ending port number. + + For cases where this field is left blank, the server will choose any port between 1024 and 65535 for passive transfer and will not perform any mapping. + - Each "mapping group type 1" specifies a single port to be used for passive transfer, and no mapping will be performed for that port. + - Each "mapping group type 2" specifies a range of ports, and all ports in that range will be used for passive transfer without any mapping. + - Each "mapping group type 3" specifies a listening port to be used for passive transfer, and when the server selects this port, it will return the "response port number" to the client. + - "Mapping group type 4" requires that the two ranges before and after the colon `:` have equal lengths. Each "mapping group type 4" forms a one-to-one pairing of port numbers, where each pair is treated as a "mapping group type 3." + + The following are legal formats: + - `1024` + - `4001-5000,5001-6000:50001-510004000:65535` + + The following are illegal formats: + - `1023` (less than 1024) + - `65536` (greater than 65535) + - `4000, 5000` (space after the comma) + - `2000 - 3000 : 4000 - 5000` (spaces are illegal) + - `2000-2001:3000-3002` (unequal length of ranges) + + If the field is invalid, the server will choose any port between 1024 and 65535 for passive transfer without performing any mapping. + + The design of port mapping is intended to address the complexity of external port mapping. For example, if the server is running inside a Docker container and uses port 2000 for passive transfer, but Docker maps port 2000 inside the container to port 12000 on the host machine, you can achieve this mapping using the configuration `12000:2000`. + +```mermaid +sequenceDiagram + participant Client + participant Port Mapping + participant Server + Client->>Server: Send EPSV command + Server-->>Client: Return 12000 + Client->>Port Mapping: Connect to port 12000 + Port Mapping->>Server: Forward to port 2000 +``` + +If there are multiple layers of port mapping between the server and the client, only the port number closest to the client needs to be specified before the `:` symbol, and only the port number closest to the server needs to be specified after the `:` symbol. For example, in the following scenario, you can fill in `2000:5000`: + +```mermaid +sequenceDiagram + participant Client + participant NAT Traversal Service + participant Soft Router Port Forwarding + participant Docker Container Port Mapping + participant OpenList Process + Client->>NAT Traversal Service: 2000 + NAT Traversal Service->>Soft Router Port Forwarding: 3000 + Soft Router Port Forwarding->>Docker Container Port Mapping: 4000 + Docker Container Port Mapping->>OpenList Process: 5000 +``` + +- FTP Proxy User-Agent Request Header + + Some storage drivers require a User-Agent request header when accessing the FTP server. You can simply use any fake value for this header. + +- Force FTP Connection to Use Explicit TLS + + Forces the use of the FTPS protocol, which only encrypts the data stream and not the control stream. + If the "Enable FTP Implicit TLS" option is enabled, this option will be ignored. + + If no valid TLS private key and certificate are provided, and this option is not enabled, the server will only accept the FTP protocol. + + If valid TLS private key and certificate are provided, but this option is not enabled, the server will accept both the FTP and FTPS protocols. + + If no valid TLS private key and certificate are provided, but this option is enabled, the FTP server will fail to start. + +- Enable FTP Implicit TLS + + Uses the FTPS protocol, which encrypts both the data stream and the control stream. This makes it incompatible with FTP and FTPS (explicit) protocols. + + When this option is enabled, the "Force FTP Connection to Use Explicit TLS" option will be ignored. + If no valid TLS private key and certificate are provided but this option is enabled, the FTP server will fail to start. + +- FTP TLS Private Key Path + + The path to the TLS private key file. Leaving it empty or providing an invalid path means TLS will not be enabled. + + Enabling TLS may require the client to access the server using a domain name, though the "FTP Server Public Address" can still be an IP address. + +- FTP TLS Certificate Path + + The path to the TLS certificate file. Leaving it empty or providing an invalid path means TLS will not be enabled. + +## SFTP Configurations + +| Field | Meaning | Example Value | +| ------ | ------------------------------ | --------------------------------------------------------- | +| enable | Whether enabled | `true` / `false` | +| listen | (Allowed access IP mask)\:port | `":5222"` (default) / `"0.0.0.0:22"` / `"127.0.0.1:2222"` | diff --git a/pages/en/guide/advanced/index.md b/pages/en/guide/advanced/index.md new file mode 100644 index 000000000..e69de29bb diff --git a/pages/en/guide/advanced/ipa.md b/pages/en/guide/advanced/ipa.md new file mode 100644 index 000000000..7bfd61b04 --- /dev/null +++ b/pages/en/guide/advanced/ipa.md @@ -0,0 +1,98 @@ +--- +categories: + - guide + - advanced +top: 70 +--- + +# IPA install + +## 1. IPA native installation + +You can directly upload the ipa file. +But for **ios>=16**, you need to name the ipa file: `original file name@bundle-identifier.ipa`, that is, you need to add `@bundle-identifier` on the original basis, in which `bundle-identifier` is the `CFBundleIdentifier` of Info.plist in the IPA package. Such as: + +- aszs@rn.notes.best.ipa + Recommended to use [ipa-renamer](https://github.com/Xhofe/ipa-renamer) to rename your ipa files automatically and quickly. +- If you are a mobile phone user, you can also rename the IPA file, but you must know the format mentioned above, but there is no mobile phone renaming software, please solve it yourself... + +### Tips + +- **.ipa installation requires https and a valid certificate** , not just a package can be installed without a signature + - How to change the name of the computer version + + + + - How to change the name of the IOS 16 mobile phone + + + +**Installation Demo**: Deploy **HTTPS** and **IPA files** are signed prerequisites, if **IOS16 rename by yourself** If you can’t install it yourself, please check the problem yourself + +- **Installation demo environment**: IOS15.4.1 iPhone12PM, personal certificate, HTTPS + + + +## 2. TrollStore install + +**Requires OpenList version ≥ 3.12.2 and above**,Devices that support the installation of TrollStore (jailbroken devices are not explained here) + +For more devices, please see the table below. The content of the chart comes from [**TrollStore**](https://github.com/opa334/TrollStore) + +Chart information update is uncertain, you can go to view https://ios.cfw.guide/installing-trollstore + +| From | To | **arm64 (A8)** | arm64 (A9-A11) | arm64e (A12-A17/M1-M2) | +| :--------------------: | :--------------------: | :--------------------------------------------------------------------------------: | :----------------------------------------------------------------------------: | :----------------------------------------------------------------------------: | +| 14.0 beta 1and earlier | 14.0 beta 1and earlier | Unsupported | Unsupported | Unsupported | +| 14.0 beta 2 | 14.8.1 | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | +| 15.0 | 15.5 beta 4 | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | +| 15.5 | 15.5 | [TrollInstallerMDC](https://ios.cfw.guide/installing-trollstore-trollinstallermdc) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | +| 15.6 beta 1 | 15.6 beta 3 | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | +| 15.6 beta 4 | 15.6.1 | [TrollInstallerMDC](https://ios.cfw.guide/installing-trollstore-trollinstallermdc) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | +| 15.7 | 15.7.1 | [TrollInstallerMDC](https://ios.cfw.guide/installing-trollstore-trollinstallermdc) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | +| 15.7.2 | 15.8.2 | [TrollMisaka](https://ios.cfw.guide/installing-trollstore-trollmisaka) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | +| 16.0 beta 1 | 16.0 beta 3 | Not Applicable | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | +| 16.0 beta 4 | 16.6.1 | Not Applicable | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | +| 16.7 RC | 16.7 RC | Not Applicable | [TrollHelper](https://ios.cfw.guide/installing-trollstore-trollhelper) | **Coming Soon** | +| 16.7 | 16.7.8 | Not Applicable | Unsupported | Unsupported | +| 17.0 beta 1 | 17.0 beta 4 | Not Applicable | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | **Coming Soon** | +| 17.0 beta 5 | 17.0 | Not Applicable | [TrollHelper](https://ios.cfw.guide/installing-trollstore-trollhelper) | **Coming Soon** | +| 17.0.1 and later | 17.0.1 and later | Not Applicable | Unsupported | Unsupported | + + IPA Resources :gift: + +### Installation example + +If you want to use the installation is very simple, click the **TrollStore** button to install. + +![gif](https://pic.rmb.bdstatic.com/bjh/ff1e47ebc1efe5a907dbfffabf4d5f67.gif) + +### Other instructions + +1. If you click the **TrollStore** button to open Apple's magnifying glass, it is because of the following reasons + - Your `TrollStore` version number is less than version 1.3+, it is recommended to update to the latest version directly + - Your URL scheme is not enabled. If you enable it, remember to click the first option to log out immediately **(Rebuild Now)** + - ![TrollStore](/img/advanced/TrollStore.jpg) +2. What is the wake-up format of the TrollStore URL? + > `apple-magnifier://install?url=` +3. Is there any benefit to this installation? + - See the flow chart below for explanation + +```mermaid +--- +title: Is there any benefit to this installation? +--- +flowchart TB + A-->B + subgraph A[original process] + C[First download the application from OpenList]-->D[find the downloaded software] + D-->E[click on the software and select share in the upper right corner] + E-->F[find the troll icon] + F-->G[jump into the troll software to install] + G-->H[install and delete the software] + end + subgraph B[troll process] + I[Click the OpenList troll install button and click the pop-up window prompt]-->J[enter the troll app and wait for download] + J-->K[after downloading, click the install button] + end +``` diff --git a/pages/en/guide/advanced/mcp.md b/pages/en/guide/advanced/mcp.md new file mode 100644 index 000000000..1adcd9ae1 --- /dev/null +++ b/pages/en/guide/advanced/mcp.md @@ -0,0 +1,157 @@ +--- +categories: + - guide + - advanced +top: 20 +--- + +# MCP + +[MCP (Model Context Protocol)](https://modelcontextprotocol.io) is an open protocol that standardizes how applications provide context and tools to Large Language Models (LLMs). OpenList implements MCP as a **Streamable HTTP** transport endpoint, enabling AI agents and MCP clients to interact with file storage. + +With the OpenList MCP endpoint, you can: + +- List files and directories +- Get file or directory details +- Obtain download links + +## How to Enable + +1. Open the [Configuration File](../../configuration/configuration.md#mcp) +2. Set `mcp.enable` to `true`: + +```json{2} + "mcp": { + "enable": true + } +``` + +3. Restart OpenList for the change to take effect +4. The MCP endpoint is now available at `http[s]://your-domain:port/mcp` + +## MCP Client Configuration + +To connect an MCP client (such as an AI coding agent that supports MCP) to OpenList, use the following configuration: + +**Transport type**: Streamable HTTP + +**URL**: `http[s]://your-domain:port/mcp` + +**Authentication**: Token Authentication. The token is your OpenList login token — include it directly in the `Authorization` header of each request (e.g. `Authorization: `). Do **NOT** add a `Bearer` prefix. Obtain the token from your OpenList account settings page or via the API. + +::: warning +OpenList's authentication middleware reads the `Authorization` header value as-is. Adding a `Bearer ` prefix will result in authentication failure. Use `Authorization: ` only. +::: + +**Protocol version**: `2025-11-25` (also compatible with `2025-06-18`) + +**Session handling**: A session is created on `initialize` and identified via the `MCP-Session-Id` response header. You must include this header in subsequent requests. + +## Available Tools + +### `openlist.fs.list` + +List files and directories under a mount path that the current user can access. + +**Parameters**: + +| Parameter | Type | Required | Description | +| ---------- | --------- | -------- | ---------------------------------------------- | +| `path` | `string` | Yes | Mount path to list, e.g. `"/"` or `"/movies"` | +| `refresh` | `boolean` | No | Refresh the directory listing before returning | +| `password` | `string` | No | Optional password for protected paths | +| `page` | `integer` | No | 1-based page number (default: 1) | +| `per_page` | `integer` | No | Page size (default: all items) | + +**Response**: Returns file list with name, size, type, modification time, thumbnail, hash info, and storage details. + +### `openlist.fs.get` + +Get file or directory details for a mount path that the current user can access. + +**Parameters**: + +| Parameter | Type | Required | Description | +| ---------- | -------- | -------- | ------------------------------------------------ | +| `path` | `string` | Yes | Mount path to inspect, e.g. `"/movies/demo.mp4"` | +| `password` | `string` | No | Optional password for protected paths | + +**Response**: Returns file details including name, size, type, modification time, raw URL, readme, header, provider, and related files at the same level. + +### `openlist.fs.link` + +Return usable link information for a file path that the current user can access. + +**Parameters**: + +| Parameter | Type | Required | Description | +| ---------- | -------- | -------- | ----------------------------------------------- | +| `path` | `string` | Yes | File mount path, e.g. `"/movies/demo.mp4"` | +| `password` | `string` | No | Optional password for protected paths | +| `type` | `string` | No | Optional link type forwarded to storage drivers | + +**Response**: Returns link information including direct URL, proxy URL, download URL, HTTP headers, content length, concurrency, and part size (for multi-part downloads). + +## Protocol Details + +### Transport + +OpenList implements the MCP **Streamable HTTP** transport. The endpoint accepts: + +- `POST /mcp` — Main endpoint for all MCP JSON-RPC requests +- `GET /mcp` — Returns `405 Method Not Allowed` with `Allow: POST, DELETE` (used for CORS preflight validation) +- `DELETE /mcp` — Terminates an active session (requires `MCP-Session-Id` header) + +### Authentication + +The MCP endpoint reuses OpenList's authentication middleware. Requests must include a valid `Authorization` header with a valid user token. The session is bound to the authenticated user — a user cannot use sessions belonging to other users. + +### Session Lifecycle + +1. **Initialize**: Client sends `initialize` request. Server returns a `MCP-Session-Id` response header. +2. **Notify Initialized**: Client sends `notifications/initialized` to mark the session as ready. +3. **Tool Calls**: Client sends `tools/list` and `tools/call` requests with the `MCP-Session-Id` header. +4. **Ping**: Client may send `ping` to keep the session alive. +5. **Cleanup**: Sessions expire after 30 minutes of inactivity. Global maximum: 128 sessions per server, 16 sessions per user. The least recently used sessions are evicted first. + +### Protocol Version + +Supported protocol versions: `2025-11-25` (default), `2025-06-18`. + +The server returns its protocol version during `initialize` negotiation. Subsequent requests must carry a compatible `MCP-Protocol-Version` header matching the negotiated version. + +### Error Codes + +| Code | Meaning | +| ------ | ---------------------------------- | +| -32700 | Parse error | +| -32600 | Invalid request | +| -32601 | Method not found | +| -32602 | Invalid params | +| -32603 | Internal error | +| -32000 | Bad request (missing headers, etc) | +| -32001 | Session/user not found | +| -32002 | Session not initialized | +| -32003 | Permission error | + +## Example: Using with AI Coding Agents + +Many AI coding assistants support the MCP protocol and can be configured to use OpenList as a file system tool. Below is an example configuration (e.g., for VS Code or Claude Desktop): + +```json +{ + "mcpServers": { + "openlist": { + "type": "http", + "url": "https://your-domain:port/mcp", + "headers": { + "Authorization": "YOUR_OPENLIST_TOKEN" + } + } + } +} +``` + +Replace `YOUR_OPENLIST_TOKEN` with your actual OpenList authentication token, which can be obtained from the OpenList management panel. + +> **Warning**: Do **NOT** prefix the token with `Bearer `. The `Authorization` header value must be the token itself, nothing else. diff --git a/pages/en/guide/advanced/meta.md b/pages/en/guide/advanced/meta.md new file mode 100644 index 000000000..2fdf1ee7e --- /dev/null +++ b/pages/en/guide/advanced/meta.md @@ -0,0 +1,83 @@ +--- +categories: + - guide + - advanced +top: 80 +--- + +# Meta + +Most settings in meta information apply to `All Roles`. The `Read Users` and `Write Users` fields are exceptions — they allow you to restrict access to specific users. + +## Path + +The path for this meta to take effect. + +## Password + +Password required to access this path. + +- Meta information password will not take effect when accessed using `WebDav` + +::: danger Notes +If you want the password to be effective in subfolders, please check `Apply to sub folder` after the password. Do not check `Write` and then check `Apply to sub folder` + +The correct check example is shown below. If you configure the options correctly, if you check the wrong option `Write` and then check `Apply to sub folder`, it will cause anyone to have permission to write dangerous operations + +![](/img/advanced/meta/password_b.png#light) + +![](/img/advanced/meta/password_h.png#dark) + +::: + +## Read Users + +A whitelist of users allowed to read this path. If the list is non-empty, only the selected users can access the path; all other users will be denied access. + +- Users are selected by name in the management interface +- Check `Apply to sub folder` to also restrict sub-directories + +## Write Users + +A whitelist of users allowed to write to this path. If the list is non-empty, only the selected users can perform write operations (upload, create, rename, move, delete) on this path. + +- Users are selected by name in the management interface +- Check `Apply to sub folder` to also restrict sub-directories + +## Write Content Bypass + +Allow any user to make directory, create new file and upload files, bypassing user-level write permission checks. + +## Hide + +The objects to hide of this path, One regular expression (in `Golang`) per line + +- Meta information hiding can take effect when accessed using `WebDav` + +## Readme + +The Readme to render while enter this path, support markdown content or markdown link. + +- Show at bottom of list +- The automatically rendered file name is: **`readme.md`** + +## Header + +The Readme to render while enter this path, support markdown content or markdown link. + +- Show at top of list +- The automatically rendered file name is: **`top.md`** + - Files can not be displayed in the list, for example `readme.md` is not displayed in the list + - Backstage --> Settings --> Global --> Hidden files --> Add newline `/\/top.md/i` + +## Apply to sub folder + +Apply this meta to sub folder of specific path + +## :warning: Tips + +Regarding hidden, users without permissions can search for hidden folders/files, solutions: + +:white_check_mark: If you want to hide the folder in a folder, create a new Yuan information alone, and select the folder we want to hide,,Hidden if you want to hide everything, write directly`.*` + +:x: You cannot directly fill in the meta information of the root directory `/`, and then fill in the folder we want to hide, the error case [View details](https://github.com/alist-org/alist/issues/4494) > ![](/img/advanced/hide-tips.png) diff --git a/pages/en/guide/advanced/offline-download.md b/pages/en/guide/advanced/offline-download.md new file mode 100644 index 000000000..e32cbaccd --- /dev/null +++ b/pages/en/guide/advanced/offline-download.md @@ -0,0 +1,198 @@ +--- +categories: + - guide + - advanced +top: 50 +--- + +# Offline download + +## Please read this tip before use + +::: warning Please read the following tips carefully +There are two kinds of software with offline download function + +The two usages are the same, and the software configuration is set in advance (see the following instructions for specific configuration), + +Then go back to the front-end interface, **find the folder you want to upload offline files to, and you can manually enter the folder** + +Then find the offline download button in the lower right corner, and choose to use Aria2 download or qBittorrent download (>v 3.11.0) + +If you use **docker**, please map the following two default paths by yourself. (mapped to the same directory on the host machine) + +- **/opt/openlist/data/temp/aria2** +- **/opt/openlist/data/temp/qBittorrent** + +::: + +::: danger OpenList ≥ 3.42.0 +OpenList Version ≥ 3.42.0 View [Allow all cloud disks to call other cloud disks' offline download tools](../../configuration/other.md) + +- ≥ 3.42.0 If you want to use offline download, you need to configure a temporary folder in the background before you can use it (otherwise it will only show **SimpleHttp**) +- manage ==> settings ==> other ( **https://example.com/@manage/settings/other** ) + - If you want to [use/configure] PikPak for offline downloading + 1. Mount PikPak storage + 2. Set Pikpak temp dir in the background + 3. Select any folder of this account as a temporary directory + - If multiple PikPaks are mounted and you want to use that account for offline downloading, then select the directory of that account as the temporary directory. + +**The following applies only to configuration instructions** + +::: + +### Aria2 + +[**Click to view instructions for use**](../../configuration/other.md) + +### qBittorrent + +#### Docker Compose + +```yaml +# docker-compose.yml +services: + openlist: + image: 'openlistteam/openlist:latest' + container_name: openlist + volumes: + - ./config/openlist:/opt/openlist/data + - ./temp/qBittorrent:/opt/openlist/data/temp/qBittorrent + ports: + - '5244:5244' + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + restart: unless-stopped + networks: + openlist: + aliases: + - openlist + qbittorrent: + image: lscr.io/linuxserver/qbittorrent:latest + container_name: qbittorrent + environment: + - PUID=0 + - PGID=0 + - WEBUI_PORT=8080 + volumes: + - ./config/qbittorrent:/config + - ./temp/qBittorrent:/opt/openlist/data/temp/qBittorrent + ports: + - 8080:8080 + restart: unless-stopped + networks: + openlist: + aliases: + - qbittorrent +networks: + openlist: +``` + +#### Windows + +(Here we take the Windows side as an example, I don’t know if there is one on the Linux side) +First of all, we need to configure the default values on the client side of **`qBittorrent`** +According to [source code](https://github.com/alist-org/alist/blob/main/internal/bootstrap/data/setting.go#L159-L162), we know that the default values are admin:adminadmin@localhost:8080/ + +- **ip**: localhost +- **port**: 8080 +- **user**: admin +- **password**: adminadmin + +1. We fill in the above parameters into the **`qBittorrent`** client, after configuration, we go to the OpenList front end to download offline (**method refer to the description at the beginning**) + - If you are prompted after submitting the offline link: **Qbittorrent not ready**, try restarting both OpenList and qBittorrent +2. Default value configuration view address: (The link may also change position based on subsequent optimization) + - **https://github.com/OpenListGo/alist/blob/main/internal/offline_download/qbit/qbit.go#L28** + - `{ *** Value: "http://admin:adminadmin@localhost:8080/", Type: conf.TypeString, *** } ` + +3. Use **`qBittorrent`** to offline `.torrent` type files + - Although you cannot directly add offline `.torrent` type files, you can save the country with a curve Reference: [View method](https://github.com/alist-org/alist/issues/2459#issuecomment-1354870010) + +4. You can configure it yourself not to delete after the download is complete, but to do seeding, the default is `0`, and it will be deleted immediately after uploading + - Modification location: **OpenList background** --> **Settings** --> **Qbittorrent seedtime** option, set the time you need to configure, the unit is `minute`, after the set seeding time is reached, it will automatically delete + +5. **We can also customize, instead of using the default presets** + - Modification location: **OpenList Manage** --> **Settings** --> **Qbittorrent url** option, just follow the modification + ![Offline download](/img/advanced/offline-download.png) + +#### Precautions + +1. Appeared in the background qBittorrent task: **`torrent parse timeout`**, parsing timed out +2. The torrent can be parsed, but **`The system cannot find the path specified.`** appears when uploading. + + The above two methods are most likely caused by the qBittorrent software itself, probably because of the green version and the modified version that have undergone some functional castration modifications. It is recommended to go to the official website to download and install it to restore it (the conclusion drawn by myself after stepping on the pit) + - **qBittorrent** official website: **https://www.qbittorrent.org/** + - **qBittorrent-Enhanced-Edition** GitHub: **https://github.com/c0re100/qBittorrent-Enhanced-Edition** + +3. Because the `v2.8.3` API is used, the minimum version of qBittorrent should be `4.4.0beta2` or `4.3.8` official version +4. If **qBittorrent** sets the ip where alist is located to be exempt from authentication, you can directly omit the user name and password before `@` (configured on the **qBittorrent** client side) + - 3 and 4 are not mentioned in the video. + **qBittorrent** Video Tutorials + + + + **If the video fails, you can watch it here: https://b23.tv/J34qDiG** + +### SimpleHttp + +Subsequent supplement + +### Transmission + +#### Docker Compose + +```yaml +# docker-compose.yml +version: '3' +services: + openlist: + image: 'openlistteam/openlist:latest' + container_name: openlist + volumes: + - ./config/openlist:/opt/openlist/data + - ./temp/transmission:/opt/openlist/data/temp/Transmission + ports: + - '5244:5244' + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + restart: unless-stopped + networks: + openlist: + aliases: + - openlist + transmission: + image: lscr.io/linuxserver/transmission:latest + container_name: transmission + environment: + - PUID=0 + - PGID=0 + - TZ=Etc/UTC + volumes: + - ./config/transmission:/config + - ./temp/transmission:/opt/openlist/data/temp/Transmission + ports: + - 9091:9091 + restart: unless-stopped + networks: + openlist: + aliases: + - transmission +networks: + openlist: +``` + +### 115 Cloud、PikPak、Thunder + +Versions v3.42.0 and above support calling the offline download function in OpenList + +After setting other configuration temporary directory options in the background, select the offline download option in the lower right corner of the front-end page to select the corresponding driver offline download function + +- Support:`magnet`, `http`, `ed2k` links + - PikPak: Also supports: X, TikTok, Facebook, TG URL links +- Some tips for using 115 offline downloads: + 1. Out of sync problems may occur (manual refresh in the lower right corner ) + 2. Currently, when the download is successful, completed tasks in the offline list are deleted. + 3. 115 Task URLs that are already in the offline list cannot be added again. diff --git a/pages/en/guide/advanced/rapid_upload.md b/pages/en/guide/advanced/rapid_upload.md new file mode 100644 index 000000000..74839e241 --- /dev/null +++ b/pages/en/guide/advanced/rapid_upload.md @@ -0,0 +1,45 @@ +--- +categories: + - guide + - advanced +top: 130 +--- + +# Rapid upload + +## Introduce + +**Rapid upload** means that files between two cloud disks can be uploaded to each other quickly without waiting. Here is an introduction to what can be mutually Rapid uploaded +**There is a premise that the files required for Rapid upload in network disk A are already available in network disk B, so that Rapid upload can be performed, otherwise it will be uploaded normally** + +::: tip +**The left side** is the source Network Disk、**The top side** is the target Network Disk +::: + +| | Aliyun | 115 | Baidu(5) | 189 | Xunlei | PikPak | GoogleDrive | OneDrive | localhost | +| :---------: | :----------------: | :----------------: | :-----------------: | :----------------: | :-------------: | :----------------: | :-------------: | :-------------: | :-------------: | +| localhost | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | +| Aliyun | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| 115 | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| Baidu | :x: | :x: | :question: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| 189 | :x: | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| Xunlei | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | +| PikPak | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | +| GoogleDrive | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| OneDrive | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :no_entry_sign: | + +1. The **Aliyun** mentioned here refers to [**aliyundriveOpen**](../../guide/drivers/aliyundrive_open.md) +2. **OneDrive** has not been tested because there is no account. If the test is valid and there are no problems, feedback is welcome. +3. **Aliyun** and **189**: Both need to turn on the Rapid upload option in the storage configuration, otherwise Rapid upload cannot be performed +4. **GoogleDrive** files can be transferred to **189** in seconds and **189** can transfer files to **189** in seconds. There may be some problems and are not very stable. Need to pay attention to observation +5. **Baidu Net Disk** can only be stored from local storage Rapid Upload to the net disk. If you use cross-storage to Copy Rapid Upload, you can only download the file first and then perform Rapid Upload. + +### How to use + +1. Right-click the file you want to transfer in the list, click Copy, and then select the folder to transfer to. +2. If there are multiple files, you can select the entire folder or open the checkbox in the lower right corner to select multiple files, and then there is a row of buttons at the bottom to click to copy. + +
+ Rapid_upload + Rapid_upload +
diff --git a/pages/en/guide/advanced/s3.md b/pages/en/guide/advanced/s3.md new file mode 100644 index 000000000..bc7eb15ba --- /dev/null +++ b/pages/en/guide/advanced/s3.md @@ -0,0 +1,57 @@ +--- +categories: + - guide + - advanced +top: 40 +--- + +# S3 + +This function is to map OpenList as the S3 protocol. You can use the [S3 client](#s3-client-recommendation-fill-in-sample), just as OpenList can be mapped to WebDAV. + +- It can be the entire OpenList (root directory) or a separate folder + +## 1. How to use + +1.1 If it is for the first time, first open OpenList Manage to generate `S3 access key id` and `S3 secret access key` to save, then stop OpenList, open the configuration file to enable the `S3` service + +- If you update ID and secrets in the future, update and save, and then restart OpenList to take effect + +![](/img/advanced/s3/s3_config.png) + +1.2 Open the [Configuration File](../../configuration/configuration.md#s3), change the `enable` field to` true` to enable S3 service + +```json{2} + "s3": { + "enable": true, + "port": 5246, + "ssl": false + } +``` + +1.3 After the first two steps are completed, Start OpenList, open the Manage `s3` configuration page, and choose to store a certain path as an object to store the barrel + +- On the left is the name of the storage bucket displayed outside, and the right is the existing list directory path of OpenList. If you want to map the entire root directory, you can choose the `root` path in the list + - There may be errors in manual filling. It is recommended to use the `Choose` button on the right side for selection + ![](/img/advanced/s3/s3_add_backup.png) + +## 2. Use instructions + +1. For ordinary object downloads, the transfer mode depends on the storage and proxy settings: + - If proxying is not required and the storage provides a direct download URL, OpenList may return an HTTP 302 redirect to the storage provider. + - If **Web Proxy** is enabled for the target storage, or another driver or proxy rule requires proxying, OpenList serves the response body through the local server. +2. A direct redirect saves OpenList bandwidth, but the S3 client must follow a cross-host redirect and accept the provider URL's access requirements. Server relay uses OpenList bandwidth and is a safer starting point for clients that cannot use the direct provider redirect. For repository tools such as restic or rustic, enable **Web Proxy** unless direct mode has been verified. +3. Currently only supports the use of client operations `List`, `get`, `put`, `del`, other operations are not supported yet +4. Currently only supports the use of the `S3 Client` to operate, Mount S3 to the `OpenList Object Storage` temporarily unavailable + +## 3. S3 client recommendation/fill in sample + +### 3.1. S3 Browser + +S3 Browser:[https://s3browser.com](https://s3browser.com/download.aspx) + +![](/img/advanced/s3/s3browser.png) + +### 3.2. S3Drive + +![](/img/advanced/s3/s3drive.png) diff --git a/pages/en/guide/advanced/search.md b/pages/en/guide/advanced/search.md new file mode 100644 index 000000000..b94d4cebb --- /dev/null +++ b/pages/en/guide/advanced/search.md @@ -0,0 +1,236 @@ +--- +categories: + - guide + - advanced +top: 100 +--- + +# Search + +### how to use + +::: danger Follow the steps below to enable search: + +1. Go to the `indexes` page to select a **Search index** and click `Save`. +2. After the index is saved, click `Build indexes` to build the index. +3. Now you can search for files by click the search block on the top right corner of the page or by using the shortcut `Ctrl + K`. + +:exclamation: If you do not follow the above prompts, the prompt will be opened: **Search Not available** +::: + +## Difference between different search indexes + +- `database`: Search by database, which is using the existing data.db. It will create a new table, record the parent directory, name, and size of every object, but the search does not split words which means that match whether the keywords you enter appear in the name of object. In general, if you don't have a specific search requirement, we recommend you choose it. + +- `database (non-full-text search)`: The full-text search mode is used above, but full-text search will have some strange problems when using **MySQL database** as an OpenList database, which has not been resolved yet, so if your OpenList database Change to **MySQL**, and your OpenList version **`≥3.9.1`** It is recommended that you use this to build an index, although it is slower than full-text search and the gap is not very big, but it will not search for strange files , it’s more secure. After the future version is repaired, we will inform you to use the new full-text search to build the index. If you are using **sqlite3**, you can use whichever you like. + +- `bleve`: An open source full-text search engine. It will split the words in the name of object and search for the keywords you enter. But its search results may be so strange that you can't get the results you want, and it will take up more resources. + +- **sqlite3** is easy to trigger `database is locked` lock library cannot write files + - Solution to `database is locked`: + - It's because the database is building the index. If you are still in the building process, please wait patiently. + - If the index has been completed, it is caused by turning on [Automatically update the index](#automatically-update-the-index). Please turn off [Automatically update the index](#automatically-update-the-index). If the problem still occurs, please close and restart OpenList. + - Or switch the database to MySQL + +- `meilisearch`: A feature-rich, multilingual, blazing-fast search engine written in Rust. More accurate compared to `bleve`. + Requires [self-hosting](https://www.meilisearch.com/docs/learn/self_hosted/getting_started_with_self_hosted_meilisearch) or using a cloud service. + `OpenList` uses "http://localhost:7700" as the default meilisearch host, + if you host `meilisearch` and `OpenList` together locally with `meilisearch` authentication disabled, `OpenList` will automatically connect it, + otherwise you need to modify the **meilisearch** field in the configuration file (host, index UID, API key). + When `meilisearch` instance is protected by `api key`, the minimal actions of `api key` required by `OpenList` are `["search","indexes.get","settings.*","documents.*","tasks.*"]`. + Storage Space Usage: ~800MiB per 100,000 files (including folders), which may be larger or smaller depending on filename length and folder depth. Please note that the storage space occupied by `meilisearch` will increase as files are continuously added/updated, and space will not be released even if you delete some or all documents from the index, unless you create a refresh instance. Generally, you don't need to worry too much about this situation, as the storage space usage will stabilize at a certain value with use, and will only have a significant impact on space usage when large numbers of files are added. + - Download:https://github.com/meilisearch/meilisearch/releases + - `meilisearch` Docs:https://www.meilisearch.com/docs/ + - Reference:https://github.com/AlistGo/alist/discussions/6830 + +The following table could help you understand the difference between these search indexes quickly: + +| | database(full text search) | Database (non-full-text search) | bleve | meilisearch | +| ----------------------- | ----------------------------------------------- | ------------------------------------------------------------------------ | ----------- | ----------------------------------------- | +| Search results | Can't search in Chinese | More accurate than full-text search, you can search Chinese | Fuzzy match | Support CJK tokenizers & Chinese variants | +| Search speed | Fast,see above for advantages and disadvantages | Slower than full-text search, see above for advantages and disadvantages | Fast | Blazing fast | +| Specify folder search | Yes | Yes | No | Yes | +| Disk usage | Low | Low | High | High | +| Auto incremental update | Yes | Yes | No | Yes | + +::: warning +If you are using _MySQL_ as the database, it is recommended to use **`non-full-text search`** (strongly recommended) + +**`Non-full-text search`** Although it is not as fast as full-text search, it is not much slower. If you insist on using full-text search, you may have to sacrifice the inability to search Chinese + +If you use sqlite as the database, there is no full-text search, you can choose any database~ + +Full-text search: It will not search in the text of all files, don't get it wrong. +::: + +### Deploy MeiliSearch for indexing using Docker Compose + +#### Deploy MeiliSearch using Docker Compose + +Feeling confused? + +Here's a Compose example to add `meilisearch` to your Openlist Compose. Follow the steps to set up indexing with `meilisearch`. + +```yaml +version: '3.3' +services: + openlist: + image: 'openlistteam/openlist:beta-aio' + container_name: openlist + volumes: + - '/tmp/data/Docker/OpenList:/opt/openlist/data' + ports: + - '5244:5244' + environment: + - UMASK=022 + networks: + - openlist + restart: unless-stopped + + meilisearch: + image: getmeili/meilisearch:v1.16 + container_name: meilisearch + ports: + - '7700:7700' + volumes: + - /tmp/data/Docker/meilisearch/meili_data:/meili_data + command: meilisearch --schedule-snapshot --snapshot-dir /meili_data/snapshots + environment: + - MEILI_MASTER_KEY=your_master_key_here + tty: true + stdin_open: true + networks: + - openlist + restart: unless-stopped + +networks: + openlist: + driver: bridge +``` + +Configuration Explanation: + +- `UMASK`: Sets file permissions +- `getmeili/meilisearch:v1.16`: Official recommendation to use a fixed version number (latest version at time of writing is `v1.16`). You may check the latest version yourself via the [official local deployment docs](https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally). +- Additional parameters for meilisearch can be modified via the [official local deployment docs](https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally) or deployed using alternative methods. + +::: warning +The `/tmp/data/Docker` directory here is merely an example; please store your data in an appropriate location. + +Additionally, the `MEILI_MASTER_KEY` here should be replaced with a key you generate yourself. You should substitute it with any alphanumeric string of 16 bytes or more. In most cases, one character corresponds to one byte. + +PS: This provides only a basic example. Please modify it according to your needs and add other configurations as required. +::: + +#### Setting up meilisearch in openlist + +How to configure MeiliSearch in OpenList + +First, you need to modify config.json. In this example, it should be located at `/tmp/data/Docker/OpenList/config.json`. + +Edit the section below, filling in the IP address and port of your MeiliSearch instance, along with the key you set in `MEILI_MASTER_KEY`. + +```json + "meilisearch": { + "host": "http://meilisearch:7700", + "api_key": "your_master_key_here", + "index": "openlist" + }, +``` + +Next, open the management panel, click `Indexes`, select `meilisearch`, and click `Refresh` to rebuild the index. + +![](/img/advanced/index_settings_panel.png){width=600px} + +## Search tips + +- If you want to search for a specific folder, you must choose `database` as the search index; +- If you choose `database` as the search index and the type of your database is `sqlite3`, we suggest that you don't make any changes in the admin page while building the index, as sqlite3 does not support concurrent writes and can cause `database-lock` issues; +- If you choose `bleve` as the search index, and if you want to search for new files or if you don't want to search for deleted files, the index needs to be completely rebuilt to take effect because `bleve` does not support incremental updates; +- But for `database`, it supports incremental updates, so you can search for new files or deleted files just by access the modified folder (and click `refresh` icon if cached) without rebuilding the index, which is much more convenient than `bleve`. + +### Ignore paths + +Paths to be skipped during index building, one path per line, multiple lines can be filled + +- Example: + - `/aaa network disk` + - `/bbb network disk/ccc folder` + +If you don't want to configure this, you can turn on the `disable index` option in each driver + +## Update index + +- (formerly: the path to update the index) + After building all the indexes, or a file has a large number of file updates, but it is inconvenient to rebuild, you can use this to update the index +- Example: - /aaa network disk - /bbb network disk/ccc folder + +### Automatically update the index + +:warning: **`The default is off, and the index will not be built automatically`**. + +For example, you have already built the index, but added a **network disk mount** or **folder update** later. + +But you have already built a lot of indexes. According to the previous words, there are two methods. + +1. Go in folder by folder before building + +2. Or it is cumbersome to refactor all + + But this time, just turn on the **`Automatically build index`** button and enter the **Newly mounted network disk** or **Updated folder**, the indexed files in this directory and The folder automatically builds the index without entering a folder by folder to let him build it automatically + +- Advantages: Don't worry, all the indexes in this folder can be automatically built if there is an update into the root directory of the updated folder +- Cons: always on call ready to build + +--- + +Someone will find out that [**Path to update index**](#Path to update index) can also be updated? Can be updated but the two do not conflict. + +- [**Automatically update index**](#automatically-update-the-index): suitable for users who build indexes for all files +- [**Update Index**](#update-index): Suitable for **not** to build indexes for all files, but there are files that need to be built, manually build indexes to avoid all being indexed + +### Maximum index depth + +default 20. +The one shown outside is built manually, and the update index option selects the depth in the update index button. +Explanation: The directory can enter up to several layers. For example, if you have a folder with a depth of 30 layers, set it to 20, and only build the first 20 layers, and the remaining 10 layers will not be built. + +## :warning: Precautions for use + +- OpenList **V2** and **v3** types of mounts cannot be built by default +- If you are using **MySQL** as the database, it is recommended that you use **database (non-full-text search)**, [**Click to view details to see the second item**](#difference-between-different-search-indexes) +- In the future version (**≥3.9.0 version**), V3 users can choose whether to allow others to mount your network disk and then index it :no_entry:**`Use with caution`**:no_entry: + - View details: [allow-indexing](/en/configuration/site#allow-indexing) + - Don't ask why V2 is not supported, because the V2 version is no longer maintained, so there is no follow-up +- Why not directly open V2 V3 index construction: **https://github.com/alist-org/alist/discussions/2529** +- After building an index, users without permissions can search for hidden file/folder solutions [click to view](meta.md#tips) + +## The database file is very large, what should I do if it is still the same after clearing the index? + +Normal users do not modify the database options. They use the `sqlite` database to build indexes, which will cause the database file to be particularly large + +- Data files, `Data` folders in the same directory in OpenList program,`data.db,data.db-shm,data.db-wal` + +After turning on the constructive index, the more the number you build, the larger the files. Finally, you accidentally occupy the machine's hard disk, and then click the clear index button. What should I do if the file is still as big? + +- This is caused by the cache of `sqlite`, there are two solutions: + 1. We use commands or tools to connect to `sqlite` database, input:**`VACUUM;`** + + ```sql + VACUUM; + ``` + + 2. After using the command to clean up, we replace it with `mysql` database before constructing indexes + - Sqlite replaced with mysql database tutorial:**[BV1iV4y1T7kh](https://www.bilibili.com/video/BV1iV4y1T7kh)** + + Comparison after cleaning the command: The picture above shows before cleaning up, and the following figure shows that after cleaning, you can execute several commands several times if there is no effect. + + ![](/img/advanced/sqlite-mysql.png) + +--- + +`data.db, data.db-shm, data.db-wal` when backup, when backup,`data.db-shm,data.db-wal` Do these two files need backup? + +- In the backup, stop the program first, and then backup. You only need to backup the `data.db` database file. The other two do not need to backup + +- It may be after you stop the program`data.db-shm,data.db-wal`will automatically disappear, don't worry diff --git a/pages/en/guide/advanced/sso.md b/pages/en/guide/advanced/sso.md new file mode 100644 index 000000000..3ff88c5c5 --- /dev/null +++ b/pages/en/guide/advanced/sso.md @@ -0,0 +1,164 @@ +--- +categories: + - guide + - advanced +top: 60 +--- + +# Third-party single sign-on + +## 1. Single sign-on client id + +client ID. + +## 2. Single sign-on client secrets + +client secrets. +::: warning matters needing attention + +1. Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used +2. Only one of the four types of single sign-on can be enabled temporarily. If you used other single sign-on methods before and then changed to a new single Unbind (just one click) and then bind the single sign-on method you want to use now. +3. If you use **`GitHub,Microsoft,Google,DingTalk`**, you only need to fill in the client ID and secret key, and select the corresponding platform in the options. + - If you use **`Casdoor`** the following parameters need to be filled in, just follow the tutorial to fill in + +::: + +## 3. Registration binding single sign-on + +## + +## + +## + +## + +## + +## + +## + +## 4. SSO automatically registers as an OpenList account + +- **OpenList Version > **v3.22.1** New Features** + Before using single sign-on to register as an OpenList account, we need to bind the single sign-on of OpenList first, the binding method is explained above +- Supports the `five` single sign-on methods mentioned above +- Do not use the default organization (**app-built-in**) directly, because all users in this organization are global administrator accounts +- Except `CASDOOR`, you only need to fill in the` Client id` and the `Client secrets`and the newly added single login account registered as OpenList account configuration + +### 4.1. SSO Full Fill Demo + +Please refer to the detailed description below for how to fill in, the schematic diagram is just a reference for filling in and not suitable for everyone’s user habits + +![](/img/advanced/sso-add.png) + +### 4.2. SSO auto register + +If we want SSO single sign-on to be registered as an OpenList account, we need to enable this option before it can be used + +### 4.3. SSO default dir + +That is to say, the default path used by the registered account is equivalent to the `Base path` in the OpenList user settings. + +It can be the root directory `/`, or the path `/path/test/Demo` specified by the user + +![](/img/advanced/sso-dir.png) + +### 4.4. SSO default permission + +It is equivalent to which permissions are enabled by default for registered users, as shown below + +![](/img/advanced/sso-permission_b.png#light) + +![](/img/advanced/sso-permission_h.png#dark) + +The default is 0, no permission is enabled + +If we need to enable some permissions during registration, we only need the sum of the numbers of different permissions. + +For example: + +1. We need to open the user's `WebDav reading` and `WebDav manage ` by default, that is 256+512=768, we just fill in `768` in the options +2. If we need to open the three permissions of `Make dir or upload`, `Rename` and `Delete` by default when registering, then it is 8+16+128=152, we can fill in `152` in the background + I won’t say much about the examples, just add the permissions you need + +### 4.5. Precautions and instructions + +**4.1-There is already this user in the OpenList user database** + +| username | password | base\*path | role | permission | opt_secret | github_id | disabled | sso_id | +| ------------------ | -------- | ---------- | ---- | ---------- | ---------- | --------- | -------- | ----------- | +| anyi | FzdDfkmU | /test1 | 0 | 3 | | | 0 | | +| anyi**\*dc188911** | RUCtgqCw | /test1 | 0 | 3 | | | 0 | **dc18891** | + +As shown in the above table, a string of redundant ids is added after the newly registered single sign-on user name + +This is because the same user already exists in the OpenList user database, so the sso_id is also added after the user name + +If your newly registered single sign-on user name does not exist in the OpenList user database, it will not add the sso_id after the name + +**4.2-What should I do if I don’t want the SSO account to be registered as an OpenList account?** + +Just turn `SSO auto register` off,This will not affect the use of accounts that have been registered using Sso + +**4.3-If I turn off the single sign-on option, what should I do with the account registered with Sso?** + +Don't worry, after using single sign-on to register and log in to OpenList, log in in the background, and find `personal information` after logging in + +- You can modify **`username`** and **`password`** by yourself, save it after modification, so that you can log in with the OpenList account normally +- At this time, you can click `Unbind Single Sign-On Platform`, you can unbind or not unbind, and the subsequent [**default path**](#sso-default-dir) and [**default permissions**](#sso-default-permission) of this user can only be modified by the administrator in the OpenList background user + +**4.4-Why is this error code displayed when using sso?** + +```json{3} +{ + "code": 400, + "message": "The single sign on platform is not bound to any users: record not found", + "data": null +} +``` + +This is because [Single Sign-On Automatic Registration](#sso-auto-register) is not enabled for the OpenList account, and the single sign-on cannot be registered as an OpenList account + +- If you are an administrator, you can turn it on + +- If you are a user, you can contact the administrator to enable + +## 5. Sso compatibility mode + +OpenList single -point login is bound to Dingtalk, and then opens OpenList in the **`Dingtalk`** application, When you log in, choose Dingtalk login and jump to your computer browser. After authorization, you find that there is no response, because the browser cannot jump to the Dingtalk application inner browser page + +At this time, you need to open the **`sso compatability mode`** again to click on the login to log in successfully + +**OpenList applied in Dingtalk and opened as shown in the figure:** + +![](/img/advanced/sso_cm.png) + +## 6. How to create the application in Dingtalk workbench + +A picture teaches you to create a new application and add it + +- LOGO is not modified when the newly -built newly built, you can wait for the newly built and then modify See_Figure_2 +- If you just browse OpenList in the Dingtalk application, you do n’t need a single -point login management, you do n’t need to set up a single -point login configuration, you can just add an application + ![](/img/advanced/sso_add_app.png) + +### 6.1. Modify the application logo + +Open [**DingTalk open platform management**](https://open-dev.dingtalk.com/fe/app#/corp/app) Find your newly built app to modify the logo. + +![](/img/advanced/sso_fix_logo.png) + +## 7. redirect url + +After turning on **`Sso compatibility mode`** Then you need to log in to the corresponding software to modify the redirect url to change it to the two redirect url below + +- `GitHub` only needs to add one:`http(s)://You_Url/api/auth/sso_get_token` +- `Microsoft` and` Google` both need to be added, you can add multiple Redirect urls, so you only need to add two Redirect URLs. + +``` +http(s)://You_Url/api/auth/get_sso_id +http(s)://You_Url/api/auth/sso_get_token +``` + +If you enable the **Sso compatibility mode** if you do not modify it, you will prompt `Invalid Redirect URL` Error diff --git a/pages/en/guide/advanced/sso/01-GitHub.md b/pages/en/guide/advanced/sso/01-GitHub.md new file mode 100644 index 000000000..a9506f397 --- /dev/null +++ b/pages/en/guide/advanced/sso/01-GitHub.md @@ -0,0 +1,30 @@ +### 3.1. Github + +::: warning +If you want to use GitHub to log in, you first need the machine you built OpenList to be able to connect to GitHub before you can call and use it, otherwise you cannot use it if the link is not connected +::: + +Open **https://github.com/settings/developers** Click **`New OAuth App`** + +#### 3.1.1. Register OAuth Instructions + +- Application name + - Write whatever you want to call it +- **Homepage URL** + - home URL address + - Both **http** and **https** can be used +- Application description + - write whatever you want +- **Authorization callback URL** + - Callback URL address + - **https://your_domain/api/auth/sso_callback** - Both **http** and **https** can be used + Remember to get **Client secrets** after filling it out, and then fill it in the OpenList background. + Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used + +#### 3.1.2. Completely fill in the reference schematic + +![sso](/img/advanced/github.png) + +#### 3.1.3. GitHub login Video Tutorials + + diff --git a/pages/en/guide/advanced/sso/02-Dingtalk.md b/pages/en/guide/advanced/sso/02-Dingtalk.md new file mode 100644 index 000000000..01f038e34 --- /dev/null +++ b/pages/en/guide/advanced/sso/02-Dingtalk.md @@ -0,0 +1,24 @@ +### 3.2.Dingtalk + +First open **https://open-dev.dingtalk.com/fe/app#/corp/app** + +In the upper right corner, first select `New Application`, select `H5 Micro Application` as the type, fill in the content by yourself and click Confirm to create + +Click on the new application and we will see the application credentials option, where `AppKey` is the client ID, and `AppSecret` is the client secret key + +- Just fill in the corresponding parameters in the OpenList background single sign-on + Go to the left column and find `Login and Share` \*\*Fill in the callback parameters `http://127.0.0.1:5234/api/auth/sso_callback` + +```bash title="Callback" parameter example +http://127.0.0.1:5244/api/auth/sso_callback +``` + +- Note: I used the callback parameter here for local testing. http://127.x When you use it, Write **http(s):\//your own domain name/api/auth/sso_callback** when filling in and using it by yourself + + Write the callback parameters well. Let’s go to the left column and find `Privilege Management`, find **`Personal Information Read Permission of Address Book`** and click to authorize + + Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used + +#### 3.2.1. Completely fill in the reference schematic + +![sso](/img/advanced/dingding.png) diff --git a/pages/en/guide/advanced/sso/03-Microsoft.md b/pages/en/guide/advanced/sso/03-Microsoft.md new file mode 100644 index 000000000..6710a8c60 --- /dev/null +++ b/pages/en/guide/advanced/sso/03-Microsoft.md @@ -0,0 +1,33 @@ +### 3.3. Microsoft + +First open **https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade** + +Then register to create an application, I won't say much here, you can see the complete schematic diagram + +Account type must be selected: **Account in any organizational directory (any Azure AD directory - multi-tenant)** + +Microsoft callback parameters: must start with "HTTPS" or "http://localhost (I used localhost here for local testing) + +```bash title="Callback" parameter example +http://localhost:5244/api/auth/sso_callback?method=sso_get_token +http://localhost:5244/api/auth/sso_callback?method=get_sso_id +``` + +- Write **http(s):\//your own domain name/api/auth/sso_callback?method=sso_get_token** when filling in and using it by yourself + +- Write **http(s):\//your own domain name/api/auth/sso_callback?method=get_sso_id** when filling in and using it by yourself + +- Note: When adding a redirect URL to a new application, only one can be added. After the application is registered and registered, click on the application to see the options behind the redirect URI + + After filling it out, we click `Certificate and Password` on the left column to create a new `Client Password` to get our client secret key + +- After the client password is created, the **`value`** parameter is our `client key`, remember to save it, it will not appear again if it appears once, if you don’t save it in time, just create a new client password + + The client ID is in `Overview` at the top of the left column, find the application (client) ID, which is the client ID we need to fill in the OpenList + +- We have got the client ID and secret key and fill them in the OpenList single sign-on configuration. + Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used + +#### 3.3.1. Completely fill in the reference schematic + +![sso](/img/advanced/weiruan.png) diff --git a/pages/en/guide/advanced/sso/04-Google.md b/pages/en/guide/advanced/sso/04-Google.md new file mode 100644 index 000000000..0e5cb6649 --- /dev/null +++ b/pages/en/guide/advanced/sso/04-Google.md @@ -0,0 +1,36 @@ +### 3.4. Google + +::: warning +If you want to use Google to log in, you first need the machine you built OpenList to be able to connect to Google before you can call and use it, otherwise you cannot use it if the link is not connected +::: + +1. First open **https://console.cloud.google.com/projectselector2/apis/dashboard?hl=zh-cn** +2. If you are using it for the first time, you need to create a new project first (just write whatever you want, skip it if it has already been created) +3. Then configure the [consent screen](#agree-to-screen-configuration) (there is a separate instruction on how to configure the document, if it is already configured, skip it) +4. After configuring the unified screen, we click the credentials on the left, create credentials, and select OAuth client ID + +- Application Type Select Web Application, and write the name as you like +- Then add our two callback parameters in the authorized redirect URI + +```bash title="Callback" parameter example +http://127.0.0.1:5244/api/auth/sso_callback?method=get_sso_id +http://127.0.0.1:5244/api/auth/sso_callback?method=sso_get_token +``` + +- Write **http(s):\//your own domain name/api/auth/sso_callback?method=get_sso_id** when filling in and using it by yourself +- Write **http(s):\//your own domain name/api/auth/sso_callback?method=sso_get_token** when filling in and using it by yourself + After filling it out, click Create to get the OAuth client ID and secret key +- (It doesn't matter if you accidentally close it here, just click on the name of the application we created and enter it in the upper right position to see it) + We have got the client ID and secret key and fill them in the OpenList single sign-on configuration. + Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used + +#### 3.4.1. Agree to screen configuration + +If it has been configured, just ignore this picture (you can zoom in if you can’t see it clearly) +![google-oauth-00](/img/drivers/google/google-oauth-eng-00.png) +![google-oauth-01](/img/drivers/google/google-oauth-eng-01.png) +![google-oauth-02](/img/drivers/google/google-oauth-eng-02.png) + +#### 3.4.2. Completely fill in the reference schematic + +![sso](/img/advanced/google.png) diff --git a/pages/en/guide/advanced/sso/05-Casdoor.md b/pages/en/guide/advanced/sso/05-Casdoor.md new file mode 100644 index 000000000..faaff2c78 --- /dev/null +++ b/pages/en/guide/advanced/sso/05-Casdoor.md @@ -0,0 +1,33 @@ +### 3.5. Casdoor + +What is `Casdoor`? Yes, you can use other OAuth applications to log in. [**Self-deployment**](https://casdoor.org/docs/basic/server-installation) is open source and free, and official hosting requires payment. + +Now, Casdoor supports many OAuth application providers, as many as dozens of kinds, you have seen and not seen + +**GitHub open source link: https://github.com/casdoor/casdoor** + +After we enter `Casdoor`, we first create **Organization**1, **Token**2, **Application**3, **User**4 + +Do not use the default organization (**app-built-in**) directly, because all users in this organization are global administrator accounts + +Then fill in the `OpenList` backstage single sign-on option one by one. The user’s parameters are ignored for the time being. They are filled in when the personal data is bound to the single sign-on. + +![sso](/img/advanced/casdoor.png) + +After filling in the above parameters, we come to the personal data and click `Bind point single sign-on platform` to bind + +Then the `Casdoor` window will pop up, we can enter our registered user name + +![sso](/img/advanced/casdoor-user.png) + +#### 3.5.1. Access some other vendors on Casdoor + +In addition to the four that `OpenList` has already connected to `GitHub Dingding Google Microsoft`, it is also connected to `QQ Baidu Feishu WeChat/Enterprise WeChat Douyin Bilibili` and so on, all [**click Check out all the manufacturers that can be accessed here**](https://casdoor.org/docs/provider/oauth/overview), of course, the four that `OpenList` has already accessed can also be added to `Casdoor` + +**View detailed tutorials on accessing other providers: [https://anwen-anyi.github.io/index/09-ssologin.html](https://anwen-anyi.github.io/index/09-ssologin.html)** + +::: details Direct iframe viewing + + + +::: diff --git a/pages/en/guide/advanced/sso/06-Authentik.md b/pages/en/guide/advanced/sso/06-Authentik.md new file mode 100644 index 000000000..502c82d67 --- /dev/null +++ b/pages/en/guide/advanced/sso/06-Authentik.md @@ -0,0 +1,49 @@ +### 3.6. Authentik + +#### 3.6.1. Authentik Setup + +**Create a Provider for OpenList** + +1. Menu -> Applications -> Providers -> Create +2. Select `OAuth2/OpenID Provider` and click next +3. Enter an application name, this guide assumes you will call the provider `OpenList` +4. Select your authorization flow. The built-in `default-provider-authorization-implicit-consent` is acceptable +5. Make note of the `Client ID` and `Client Secret` fields as provided by Authentik - save these values for later +6. For Redirect UDIs/Origins, enter the following, replacing [your.openlist.domain] with the FQDN for your OpenList installation: + +```bash title="Callback" parameters +https://your.openlist.domain/api/auth/sso_callback\?method=sso_get_token +https://your.openlist.domain/api/auth/sso_callback\?method=get_sso_id +``` + + Please note the \ character before ? as an escape character for the regex used for this URI is mandatory. + +7. Make note of the signing key selected as you will need it later. This guide assumes you will use the default `authentik Self-signed Certificate` 8. Save the new provider + +**Create an Application for OpenList** + +1. Menu -> Applications -> Application -> Create +2. Enter an application name, recommended `OpenList` +3. An application slug of `openlist` will be automatically selected for you. This guide assumes you will keep this value +4. Select the provider by name you selected in Provider Setup step 3 - `OpenList` +5. Save the new application + +**Retrieve the JWT certificate** + +1. Menu -> System -> Certificates +2. Select the `>` next to the `authentik Self-signed Certificate`. If you chose another certificate for the application, select that certificate instead +3. Click Download Certificate to get a copy of the public JWT key + +#### 3.6.2. OpenList Setup + +- **Sso login enabled:** `yes` +- **Sso login platform:** `OIDC` +- **Sso client id:** [Client ID from Authentik] +- **Sso client secret:** [Client Secret from Authentik] +- **Sso oidc username key:** `preferred_username` +- **Sso organization name:** `user` +- **Sso application name:** `user` +- **Sso endpoint name:** `https://your.authentik.domain/application/o/openlist/` + - **Note:** Replace [your.authentik.domain] with the FQDN for your Authentik installation. Mind the trailing `/` at the end of the path. If you chose a different application slug in Authentik Application setup 3, substitute that here +- **Sso jwt public key:** Open the certificate file downloaded in step 3.3 of the Authentik Application setup and paste the contents here. It will start with `-----BEGIN CERTIFICATE-----` +- **Sso compatability mode:** `no` diff --git a/pages/en/guide/advanced/sso/07-keycloak.md b/pages/en/guide/advanced/sso/07-keycloak.md new file mode 100644 index 000000000..0aeab5e7c --- /dev/null +++ b/pages/en/guide/advanced/sso/07-keycloak.md @@ -0,0 +1,90 @@ +### 3.7. Keycloak + +Keycloak is integrated via the OIDC (OpenID Connect) protocol and can serve as an identity provider for OpenList. The steps below explain how to configure Keycloak for OpenList SSO. + +#### 3.7.1. Keycloak Setup + +##### 3.7.1.1. Create Realm + +- Log in to the Keycloak admin console. +- Click `Manage realms` in the left navigation bar, select `Create Realm` to create a new realm or use an existing one. + +##### 3.7.1.2. Create Client + +After selecting the realm, click `Clients` → `Create Client`. + +- **General settings** + ![](/img/advanced/sso/keycloak-01.png) + + ::: details see the details + + | Name | Value | + | -------------------- | --------------------------------- | + | Client type | OpenID Connect | + | Client ID | OpenList (or any name you prefer) | + | Name | OpenList (or any name you prefer) | + | Description | optional | + | Always display in UI | select as needed | + + ::: + +- **Capability config** + ![](/img/advanced/sso/keycloak-02.png) + + ::: details see the details + + | Name | Value | + | ------------------------- | ---------------------------------------- | + | Client authentication | On | + | Authorization | Off | + | Authentication flow | `Standard Flow`,`Service account roles` | + | PKCE Method | Leave blank | + | Require DPoP bound tokens | Off | + + ::: + +- **Login settings** + ![](/img/advanced/sso/keycloak-03.png) + + ::: details see the details + + | Name | Value | + | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | Root URL | Leave blank | + | Home URL | Enter your OpenList access address, e.g.: https://your.openlist.domain | + | Valid redirect URIs | `https://your.openlist.domain/api/auth/sso_callback?method=get_sso_id`
`https://your.openlist.domain/api/auth/sso_callback?method=sso_get_token`
`https://your.openlist.domain/api/auth/sso_get_token`
`https://your.openlist.domain/api/auth/get_sso_id` | + | Valid post logout redirect URIs | Leave blank | + | Web origins | Leave blank | + + ::: + +##### 3.7.1.3. Retrieve JWT public key + +- In the left menu, find `Clients`, select the newly created Client to enter the settings page. +- In the Client settings page, switch to the `Keys` tab. +- Copy the content in the `Public Key` field for later use in OpenList configuration. +- If the public key is not visible, click `Generate RSA Keys` to generate it. + +##### 3.7.1.4. Retrieve Client Secret + +- In the Client settings page, switch to the `Credentials` tab. +- Copy the content in the `Client Secret` field for later use in OpenList configuration. + +#### 3.7.2. OpenList Setup + +Fill in the following parameters in OpenList's SSO configuration: + +- **SSO login enabled:** `yes` +- **SSO login platform:** `OIDC` +- **SSO client id:** Keycloak Client ID (from [Create Client](#_3-7-1-2-create-client) above) +- **SSO client secret:** Keycloak Client Secret (from [Retrieve Client Secret](#_3-7-1-4-retrieve-client-secret) above) +- **SSO oidc username key:** `preferred_username` or as per your Mapper settings +- **SSO organization name:** `master` (from [Create Realm](#_3-7-1-1-create-realm) above) +- **SSO application name:** `OpenList` (from [Create Client](#_3-7-1-2-create-client) above) +- **SSO endpoint name:** `https://your.keycloak.domain/realms/{realm-name}` (where realm-name matches the organization name) +- **SSO jwt public key:** from [Retrieve JWT public key](#_3-7-1-3-retrieve-jwt-public-key) above +- **SSO auto register as OpenList account:** Enable as needed +- **SSO compatibility mode:** `no` (enable if compatibility is required) + +Adjust claims and endpoints based on your specific Keycloak deployment if needed. +For detailed operations, refer to the [Keycloak official OIDC documentation](https://www.keycloak.org/documentation). diff --git a/pages/en/guide/advanced/strm.md b/pages/en/guide/advanced/strm.md new file mode 100644 index 000000000..b8407c602 --- /dev/null +++ b/pages/en/guide/advanced/strm.md @@ -0,0 +1,10 @@ +--- +categories: + - guide + - advanced +top: 10 +--- + +# Generate Strm + + diff --git a/pages/en/guide/advanced/user.md b/pages/en/guide/advanced/user.md new file mode 100644 index 000000000..e3f30e88c --- /dev/null +++ b/pages/en/guide/advanced/user.md @@ -0,0 +1,75 @@ +--- +categories: + - guide + - advanced +top: 140 +--- + +# User + +## Add user + +::: warning Security Notice +Adding a user may expose files and server-side capabilities to another account. Use a strong, unique password, grant only the minimum required permissions, and create accounts only for trusted users. Security incidents resulting from improper user or permission management are the administrator's responsibility. +::: + +## Username + +Username for login. + +## Password + +Password for login. +:::tip +Password is invalid for guest user. + +If you enter an incorrect password 6 times in a row, the current IP will be blocked for 30 minutes and you will not be able to enter your account and password to log in. However, it will not affect other IPs. It will only target IPs that entered 6 incorrect passwords. + +- Restarting will immediately remove the 30-minute ban time + +::: + +## Base path + +The root path that users see when he/she log in. + +Q: **How to allow a user to see multiple folder paths?** + +A: You can create a new [alias](alias.md) storage, add all the paths you need to show the user to the alias, and then point to the newly created alias storage in the user path + +## Permission + +- Can see hides: Can see the hides files and folders +- Access without password: Can access without password +- Add offline download tasks: Add offline download tasks + - ⚠️ Granting a user remote file read/write permissions also grants them the ability to access resources from the server's network context, including internal network addresses. Only grant this permission to fully trusted users. Internal network access resulting from improper permission assignment is not considered a security vulnerability. +- Mkdir or upload: Can make directory or upload files +- Rename: Can rename files and folders +- Move: Can move files and folders +- Copy: Can copy files and folders +- Delete: Can delete files and folders +- Webdav read: Can read files and folders with webdav +- Webdav manage: Can manage files and folders with webdav +- FTP read: Can read files and folders with FTP +- FTP manage: Can manage files and folders with FTP +- Read archives: Read the contents of the file in the compressed package + - After turning on this option, compressed package format files will be previewed by default (as shown in the figure below), which will consume some server traffic, but will not download them all. + - If you want to turn off the preferred preview of the compressed format, **Manage => Setting => Preview by default when opening archives**, this option is turned off, and the preference is the download mode +- Decompress: Decompress compressed package files online + ![](/img/advanced/user_read_archives_light.png#light) + ![](/img/advanced/user_read_archives_dark.png#dark) + +## Disabled + +After checking, this user will stop using it and cannot log in. The guest account is disabled by default. If you want to enable the guest account, please close it manually. + +## Tips + +1. Are you worried that visitors can see all files? [**Click to see how to set it up here**](../../faq/why.md#how-do-i-set-it-so-that-visitors-can-only-see-the-content-after-logging-in) +2. **`Guest user is disabled, login please`**: In order to protect your OpenList security, the guest access permission is closed, if you need guest access, open it yourself + - OpenList Manage --> users --> `guest` --> **Disable** uncheck +3. Question about **`Copy/Upload`** + - If you upload (including offline download and upload) large files, or upload a lot of files (hundreds or thousands), it is not recommended to use OpenList to operate, please go to the corresponding network disk official website to operate directly +4. Non-admin users can manage offline download, copy, upload and other operations in the background + - OpenList needs to be upgraded to version, and admin can also view the user's task progress and operations + ![](/img/advanced/user_manage.png) diff --git a/pages/en/guide/advanced/webdav.md b/pages/en/guide/advanced/webdav.md new file mode 100644 index 000000000..e51030720 --- /dev/null +++ b/pages/en/guide/advanced/webdav.md @@ -0,0 +1,151 @@ +--- +categories: + - guide + - advanced +top: 50 +--- + +# WebDAV + +WebDAV (Web Distributed Authoring and Versioning) is a set of extensions to the Hypertext Transfer Protocol (HTTP) that enables users to collaboratively create, edit, and manage files directly on a web server. + +OpenList can be served as a WebDAV server, allowing users to access and modify files through a web interface. + +## Permission Configuration Instructions + +To enable a specific user to use WebDAV, the following permissions must be enabled in the `User => Permissions` settings: + +1. **WebDAV Read** + - This permission must be enabled to **view and read** files and directories in WebDAV. + - If the user **only needs to view or play files**, enabling this permission is sufficient. + +2. **WebDAV Management** + - This permission must be enabled to perform **write operations** (such as create, modify, delete, etc.). + - **Enabling only `WebDAV Management` is not enough!** You must also enable `WebDAV Management` **as well as** the specific file system permissions required for the planned operations (such as `rename`, `delete`, `copy`, `create directories or upload`, etc.). + +## Basic Connection Configuration + +Use the following parameters to connect your WebDAV client: + +| Configuration Item | Value / Description | +| ------------------ | --------------------------------------------------------------------------------------- | +| **Url** | `http[s]://your-domain:port/dav/` | +| **Host** | Your domain (e.g., `openlist.example.com`) | +| **Path** | `dav` | +| **Protocol** | `http` or `https` (strongly recommend using **https** for security) | +| **Port** | The port **must be identical** to the one used for accessing the OpenList web interface | +| **Username** | The **username** you use to log into the OpenList web interface | +| **Password** | The **password** you use to log into the OpenList web interface | + +## Storage Support + + + +::: warning +Renaming during copy is not currently supported. +::: + +## Client Software + +The following is a list of software that can be used to mount or access WebDAV services, categorized by platform: + +### 🖥️ Windows + +- **File Managers / Mounting Tools:** + - [RaiDrive](https://www.raidrive.com/) (Recommended for mounting) + - [Mountain Duck](https://mountainduck.io/) (Mount as a disk) + - [rclone](https://rclone.org/) (Command line/mounting) + - [OneCommander](https://www.onecommander.com/) (File manager) + +- **Media Players (Direct Playback):** + - [PotPlayer](https://potplayer.daum.net/) + - [Kodi](https://kodi.tv/download) + - [AIMP](https://www.aimp.ru/) (Audio player) + +### 📱 Android + +- **File Managers:** + - [Solid Explorer](https://play.google.com/store/apps/details?id=pl.solidexplorer2) + - [MiXplorer](https://mixplorer.com/) (Manual APK installation required, open source) + - [X-plore File Manager](https://play.google.com/store/apps/details?id=com.lonelycatgames.Xplore) + - ES File Explorer + +- **Media Players (Direct Playback):** + - [nPlayer](https://play.google.com/store/apps/details?id=com.newin.nplayer.pro) + - [Kodi](https://kodi.tv/download) + - [Reex](https://play.google.com/store/apps/details?id=com.reex.reexplorer) + - [VLC for Android](https://www.videolan.org/vlc/download-android.html) (Open source) + +### 🍎 iOS / iPadOS + +- **Media Players / File Managers (Direct Playback / Management):** + - [VidHub](https://zh.okaapps.com/product/1659622164) + - [nPlayer](https://apps.apple.com/us/app/nplayer/id1116905928) + - [Infuse](https://firecore.com/infuse) + - [Fileball](https://apps.apple.com/us/app/fileball-file-manager-player/id1615474435) + - [zFuse Player](https://apps.apple.com/us/app/zfuse-player/id1596223161) + +### 📺 TV (Android TV / Google TV) + +- **Media Players (Direct Playback):** + - [VidHub](https://zh.okaapps.com/product/1659622164) + - [nPlayer](https://play.google.com/store/apps/details?id=com.newin.nplayer.pro) + - [Kodi](https://kodi.tv/download) + +### 🍏 macOS + +- **File Managers / Mounting Tools:** + - [Mountain Duck](https://mountainduck.io/) (Mount as a disk) + - [rclone](https://rclone.org/) (Command line/mounting) + +- **Media Players (Direct Playback):** + - [VidHub](https://zh.okaapps.com/product/1659622164) + - [Infuse](https://firecore.com/infuse) + - [IINA](https://iina.io/) (Open source) + +### 🐧 Linux + +- **Mounting Tools / Command Line:** + - [rclone](https://rclone.org/) (Recommended, feature-rich) + - `davfs2` (System-level mounting, requires configuration) + +### 📝 Note-taking Software + +- [Joplin](https://joplinapp.org/) (Supports WebDAV sync for notes, open source) + +> **Feel free to contribute!** If you find other excellent and compatible WebDAV clients, feel free to recommend them. + +## Client Configuration Examples + +The interfaces of different software vary, but the key is to correctly fill in the information from the "Basic Connection Configuration" above. + +### nPlayer (iOS/Android) + +![](/img/guide/webdav/nplayer.png) + +### Reex (Android) + +![](/img/guide/webdav/reex.png) + +### ES File Explorer (iOS & Android) + +
+ iOS + Android +
+ +### Infuse (iOS/macOS) + +![](/img/guide/webdav/infuse.png) + +### Fileball (iOS) + +![](/img/guide/webdav/fileball.png) + +### PotPlayer (Windows) + +![](/img/guide/webdav/potplayer.png) + +### Synology NAS (Add via File Station) + +![](/img/guide/webdav/nas.png) diff --git a/pages/en/guide/drivers/115.md b/pages/en/guide/drivers/115.md new file mode 100644 index 000000000..eee9c2c17 --- /dev/null +++ b/pages/en/guide/drivers/115.md @@ -0,0 +1,384 @@ +--- +top: 697 +categories: + - guide + - drivers +--- + +# 115 Cloud / Share + + + +::: tip +[**115 Official Open API**](115_open.md) +::: + +## 115 Cloud + +### Root folder ID + +Open the official website of 115 Cloud and click the string behind the url when you click into the folder you want to set, +such as , +which is `249163533602609229` + +### Cookie acquisition method + +The following message is displayed when mounting a 115 cloud. This is because 115 has removed the three client applications of `Windows, Mac, and Linux`. + +```json +{ + "state": 0, + "error": "登录失败,系统已下架!如果你有电脑端的使用需求,我们诚挚邀请你下载体验115产品专属客户端“115浏览器”或在线使用“115网页端(115.com)”,畅享智能高效云生活。", + "errno": 0, + "message": "登录失败,系统已下架!如果你有电脑端的使用需求,我们诚挚邀请你下载体验115产品专属客户端“115浏览器”或在线使用“115网页端(115.com)”,畅享智能高效云生活。", + "code": 0 +} +``` + +The cookies you have obtained from these three devices cannot be used. Please obtain the cookies from other devices again to mount them. + +#### 1. QRCode scanning method login + +::: tip +Still under active development, please stay tuned! +::: + +#### 2. Log in by manually grabbing Cookies + +The `cookie` can be obtained from the browser with your 115 account logined, or packet capture. + +::: tip +Please note that cookies should not end with `;`. +::: + +
+ +#### 3. Get Cookies Using Python Script + +Expand the detailed description to view the tutorial and script source code + +Source code from:[**https://gist.github.com/ChenyangGao/d26a592a0aeb13465511c885d5c7ad61**](https://gist.github.com/ChenyangGao/d26a592a0aeb13465511c885d5c7ad61) + +::: details View source + +```python +#!/usr/bin/env python3 +"扫码获取 115 cookie" +__author__ = "ChenyangGao " +__version__ = (0, 0, 2) +__all__ = [ + "AppEnum", "get_qrcode_token", "get_qrcode_status", "post_qrcode_result", + "get_qrcode", "login_with_qrcode", +] +if __name__ == "__main__": + from argparse import ArgumentParser, RawTextHelpFormatter + parser = ArgumentParser(description="""\ +扫码获取 115 cookie +默认在命令行输出,需要安装 qrcode: pip install qrcode + - https://pypi.org/project/qrcode/ +可以指定 -o 或 --open-qrcode 直接打开图片扫码 +""", formatter_class=RawTextHelpFormatter) + parser.add_argument("app", nargs="?", choices=("web", "android", "ios", "linux", "mac", "windows", "tv", "alipaymini", "wechatmini", "qandroid"), default="web", help="选择一个 app 进行登录,注意:这会把已经登录的相同 app 踢下线") + parser.add_argument("-o", "--open-qrcode", action="store_true", help="打开二维码图片,而不是在命令行输出") + parser.add_argument("-v", "--version", action="store_true", help="输出版本号") + args = parser.parse_args() + if args.version: + print(".".join(map(str, __version__))) + raise SystemExit(0) +from enum import Enum +from json import loads +from urllib.parse import urlencode +from urllib.request import urlopen, Request +AppEnum = Enum("AppEnum", "web, android, ios, linux, mac, windows, tv, alipaymini, wechatmini, qandroid") +def get_enum_name(val, cls): + if isinstance(val, cls): + return val.name + try: + if isinstance(val, str): + return cls[val].name + except KeyError: + pass + return cls(val).name +def get_qrcode_token(): + """获取登录二维码,扫码可用 + GET https://qrcodeapi.115.com/api/1.0/web/1.0/token/ + :return: dict + """ + api = "https://qrcodeapi.115.com/api/1.0/web/1.0/token/" + return loads(urlopen(api).read()) +def get_qrcode_status(payload): + """获取二维码的状态(未扫描、已扫描、已登录、已取消、已过期等) + GET https://qrcodeapi.115.com/get/status/ + :param payload: 请求的查询参数,取自 `login_qrcode_token` 接口响应,有 3 个 + - uid: str + - time: int + - sign: str + :return: dict + """ + api = "https://qrcodeapi.115.com/get/status/?" + urlencode(payload) + return loads(urlopen(api).read()) +def post_qrcode_result(uid, app="web"): + """获取扫码登录的结果,并且绑定设备,包含 cookie + POST https://passportapi.115.com/app/1.0/{app}/1.0/login/qrcode/ + :param uid: 二维码的 uid,取自 `login_qrcode_token` 接口响应 + :param app: 扫码绑定的设备,可以是 int、str 或者 AppEnum + app 目前发现的可用值: + - 1, "web", AppEnum.web + - 2, "android", AppEnum.android + - 3, "ios", AppEnum.ios + - 4, "linux", AppEnum.linux + - 5, "mac", AppEnum.mac + - 6, "windows", AppEnum.windows + - 7, "tv", AppEnum.tv + - 8, "alipaymini", AppEnum.alipaymini + - 9, "wechatmini", AppEnum.wechatmini + - 10, "qandroid", AppEnum.qandroid + :return: dict,包含 cookie + """ + app = get_enum_name(app, AppEnum) + payload = {"app": app, "account": uid} + api = "https://passportapi.115.com/app/1.0/%s/1.0/login/qrcode/" % app + return loads(urlopen(Request(api, data=urlencode(payload).encode("utf-8"), method="POST")).read()) +def get_qrcode(uid): + """获取二维码图片(注意不是链接) + :return: 一个文件对象,可以读取 + """ + url = "https://qrcodeapi.115.com/api/1.0/mac/1.0/qrcode?uid=%s" % uid + return urlopen(url) +def login_with_qrcode(app="web", scan_in_console=True): + """用二维码登录 + :param app: 扫码绑定的设备,可以是 int、str 或者 AppEnum + app 目前发现的可用值: + - 1, "web", AppEnum.web + - 2, "android", AppEnum.android + - 3, "ios", AppEnum.ios + - 4, "linux", AppEnum.linux + - 5, "mac", AppEnum.mac + - 6, "windows", AppEnum.windows + - 7, "tv", AppEnum.tv + - 8, "alipaymini", AppEnum.alipaymini + - 9, "wechatmini", AppEnum.wechatmini + - 10, "qandroid", AppEnum.qandroid + :return: dict,扫码登录结果 + """ + qrcode_token = get_qrcode_token()["data"] + qrcode = qrcode_token.pop("qrcode") + if scan_in_console: + try: + from qrcode import QRCode + except ModuleNotFoundError: + from sys import executable + from subprocess import run + run([executable, "-m", "pip", "install", "qrcode"], check=True) + from qrcode import QRCode # type: ignore + qr = QRCode(border=1) + qr.add_data(qrcode) + qr.print_ascii(tty=True) + else: + from atexit import register + from os import remove + from threading import Thread + from tempfile import NamedTemporaryFile + qrcode_image = get_qrcode(qrcode_token["uid"]) + with NamedTemporaryFile(suffix=".png", delete=False) as f: + f.write(qrcode_image.read()) + f.flush() + register(lambda: remove(f.name)) + def open_qrcode(): + platform = __import__("platform").system() + if platform == "Windows": + from os import startfile # type: ignore + startfile(f.name) + elif platform == "Darwin": + from subprocess import run + run(["open", f.name]) + else: + from subprocess import run + run(["xdg-open", f.name]) + Thread(target=open_qrcode).start() + while True: + try: + resp = get_qrcode_status(qrcode_token) + except TimeoutError: + continue + status = resp["data"].get("status") + if status == 0: + print("[status=0] qrcode: waiting") + elif status == 1: + print("[status=1] qrcode: scanned") + elif status == 2: + print("[status=2] qrcode: signed in") + break + elif status == -1: + raise OSError("[status=-1] qrcode: expired") + elif status == -2: + raise OSError("[status=-2] qrcode: canceled") + else: + raise OSError("qrcode: aborted with %r" % resp) + return post_qrcode_result(qrcode_token["uid"], app) +if __name__ == "__main__": + resp = login_with_qrcode(args.app, scan_in_console=not args.open_qrcode) + print() + print("; ".join("%s=%s" % t for t in resp['data']['cookie'].items())) +``` + +::: + +1. You need to install [**Python 3.11.x**](https://www.python.org/downloads/) or above +2. If the QR code cannot be displayed properly in the `CMD` and `powershell` terminals, you can use the `-o` parameter to generate an image to scan the code, or you need to install an additional terminal + 1. Use the `-o` parameter to generate the image method and scan the QR code to confirm + - ````python + python main.py wechatmini -o + ``` + The devices that can be obtained are as follows. If you do not fill in the device, the default device on the `Web` side will be used + `Web`,`android`,`ios`,~~`linux`,`mac`,`windows`~~,`tv`,`alipaymini`,`wechatmini`,`qandroid` + ```` + - `alipaymini` and `wechatmini` They are Alipay Mini Program and WeChat Mini Program. + - **Windows、Mac、Linux should not be able to use it anymore. The official client was recently removed from the shelves.** + - It is recommended to use some devices that you do not commonly use, otherwise logging in will crowd out the previous ones. + *** + 2. Additional installation terminal + - Windows Store:**https://apps.microsoft.com/detail/9n0dx20hk701?rtc=1&hl=zh-cn&gl=CN** + - GitHub Download:**https://github.com/microsoft/terminal/releases** + 3. Other ways, solve it yourself + +--- + +Execute the command and obtain the QR code. Scan the QR code on the APP to obtain `Cookie` + +##### 3.1. Direct Execution + +```shell +PS C:\Users\233\Desktop\115> python --version +Python 3.12.2 +PS C:\Users\233\Desktop\115> python main.py wechatmini +█▀▀▀▀▀▀▀█▀▀▀▀█▀███▀▀▀█▀█▀▀▀██▀█▀▀▀▀▀▀▀█ +█ █▀▀▀█ █ █▀▄▀█▄▀▀█▀█ ▄█▀▀ █▀█ █▀▀▀█ █ +█ █ █ █▀█ ▄ ▄▀▄ ▀ ▀▄ █▀▄▀█▀█ █ █ █ +█ ▀▀▀▀▀ █▀█ ▄▀▄ ▄ ▄ █ ▄▀▄▀█▀█▀█ ▀▀▀▀▀ █ +█▀██▀▀▀▀▀▀▀ ▄█ ▀█ █▀▀▀ █▀ ▄▄▄ ▀██▀█▀▀▀█ +██▄▀▀▄▀▀▀▀ ▄██▀▄██▀██▄█ █▀▀▀ ▀▀▀▄▄▀ ▄▀█ +█ ▀██▀████ ▀ ▀ ▀ █▀ ▀▀▄▄▀▄ █▄▀▄▄ ▀▀▀█ +█ ▄▀▀█▄▀█▀▀██ ▀▀▀▀ ▄▀ ▀███▀██▀▄▀▀▄▄ █ +██▄█ ▄▀▀█▄ ▀█▄▀▄▄ █ █▀ ▄▀▀ ▄▀█▀█▀█▀█ +███▄ █ ▀ ▀█ █▄ ▀▀▀▀█▀█▀█ ▄▀▀ ▄ █ ██▄█ +█ █▀▀▀█ █ ▄▄▀▄▄▀ █▄▄▀█▀ █▀▄█ ▀▀▀ ▀█▀ █ +█ █ █ █▄ ▄▀ █▀▀ ▀▄▀▀█▀▀ █ █ ▄█▀▄▄ ▀█ +█ ▀▀▀▀▀ █▀ █▄▀ ▀ ▄█▄ █▄▀▀█▄ ▀ ▀▄▄ ▄▄ █ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +``` + +Execute the command directly. The QR code will appear in the terminal for display. + +##### 3.2. Using the -o parameter + +```bash +PS C:\Users\233\Desktop\115> python --version +Python 3.12.2 +PS C:\Users\233\Desktop\115> python main.py wechatmini -o +``` + +After using the `-o` parameter, a QR code image will pop up automatically + +### Rapid upload + +- **v.3.27.0** version Enhanced Rapid upload: You can directly upload files with `Alibaba Cloud Disk Open` by copying + - The premise is to upload the file from 115 Rapid upload to Alibaba Cloud Disk Open, the file already exists in Alibaba Cloud Disk Open, otherwise it is a normal copy task. + - If you want to transfer 115 cloud disk files to Alibaba Cloud Disk in seconds, you need to turn on the Rapid upload option of Alibaba Cloud Disk, otherwise it will be uploaded in normal mode. + If you want to upload files using Miaochuan, it is recommended to build an OpenList locally on your home computer and add a `local storage` and `115 cloud disk` to copy Miaochuan to save resources. +
+ +### Offline Download + +**v3.37.0** and above versions support calling `115 Cloud` offline download function in OpenList +Select in the lower right corner and select `115 Cloud` for offline download options + +- Support: `magnet`, `http`, `ed2k` links + Only 115 Cloud is supported for offline download. If it is not 115 Cloud, the following error message will be displayed, Although the offline download prompt was successfully added, an error will be prompted in the background. +- unsupported storage driver for offline download, only 115 Cloud is supported + ![](/img/drivers/offline_download_error.png) +- Some tips for using 115 offline downloads: + 1. Out of sync problems may occur (manual refresh in the lower right corner ) + 2. Currently, when the download is successful, completed tasks in the offline list are deleted. + 3. 115 Task URLs that are already in the offline list cannot be added again. +
+ +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 115 Share + +![url_demo](/img/drivers/115/115-url_demo.png) + +`Sharing link ID`1 and `Sharing link extraction code`2 How to obtain them respectively is clear at a glance. + +### Root folder ID + +The default is empty and the entire directory file is mounted. + +The folder IDs are the root folder ID and the subfolder ID respectively. The following demonstrates how to obtain the `shared root folder directory ID`1 and `other subfolder directory ID`2 respectively. + +#### 1. shared root folder directory ID + +Open developer mode (F12) and clear all requests first. Before we enter the folder, clear all requests first. + +Let's click on the root folder to enter. There will be a new request on the right, and then select `Load` to see the ID (cid) of our folder. + +![url_demo](/img/drivers/115/115-root_cid.png) + +#### 2. other subfolder directory ID + +Open developer mode (F12) and clear all requests first. Before we enter the folder, clear all requests first. + +There will be a new request on the right, then select `Preview` and expand it to see the IDs (cid) of other subfolders. + +![url_demo](/img/drivers/115/115-file_cid.png) + +### Error Tips + +For example, the 115 sharing link shown in the picture below has expired, but the sharing link can still be opened. + +![url_demo](/img/drivers/115/115-add_error.png) + +But when adding and saving, the following error code will appear: + +`Failed init storage but storage is already created: failed init storage: failed to get share snap: json: cannot unmarshal number into Go struct field .data.shareinfo.share_state of type string ` + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/115_open.md b/pages/en/guide/drivers/115_open.md new file mode 100644 index 000000000..a2997b375 --- /dev/null +++ b/pages/en/guide/drivers/115_open.md @@ -0,0 +1,171 @@ +--- +top: 698 +categories: + - guide + - drivers +--- + +# 115 Open + +::: tip +Developed with the official [**115 Open Platform API**](https://open.115.com) +::: + + + +## 1. Necessary Conditions + +An **115** account is required. + +::: warning Note +The speed and stability are related to the local network environment, the network environment of the 115 server, and the performance of the machine running OpenList. +::: + +## 2. Preparation for Access + +### 2.1. Register an Application on the Open Platform (optional; no need to create if using the built-in key pair of OpenList/public welfare server/self-built server) + +::: tip +Please register the application according to the requirements of the 115 Open Platform. +::: + +Open Platform address: [115 Open Platform: https://open.115.com](https://open.115.com) + +### 2.2. Obtain Tokens + +1. Visit [api.oplist.org](https://api.oplist.org) **⚠️ If using a public welfare server/self-built server, visit the address of the public welfare server/self-built server**. + +2. Select **115 Network Disk Verification** from the drop-down box. + + ![115-open-01-d](/img/drivers/115/115-open-01-d.png#dark) + + ![115-open-01-l](/img/drivers/115/115-open-01-l.png#light) + +3. If you are using the built-in key pair of `OpenList (or public welfare server/self-built server)` (i.e., you do not have application information from the 115 Open Platform), please configure according to `3.1`, `3.2`, and `3.3`: + + 3.1. Check `Use parameters provided by OpenList`. + + 3.2. Leave both `Client ID (ClientID/AppID)` and `Application Secret (AppKey/Secret)` blank. + + 3.3. Click the `Get Token` button. + + ![115-open-02-01-d](/img/drivers/115/115-open-02-01-d.png#dark) + + ![115-open-02-01-l](/img/drivers/115/115-open-02-01-l.png#light) + +4. If you are using your own created OAuth client ID and secret, please configure according to `4.1`, `4.2`, and `4.3`: + + 4.1. Do not check `Use parameters provided by OpenList`. + + 4.2. Enter your `AppId` in `Client ID` and your `AppSecret` in `Application Secret`. + + 4.3. Click the `Get Token` button. + + ![115-open-02-02-d](/img/drivers/115/115-open-02-02-d.png#dark) + + ![115-open-02-02-l](/img/drivers/115/115-open-02-02-l.png#light) + +5. In the pop-up window, log in to your 115 account and authorize OpenList to access your 115 network disk. + + ![115-open-03](/img/drivers/115/115-open-03.png) + +6. After successful authorization, the page will display your `Access Token` and `Refresh Token`. Please copy and save these two tokens. + + ![115-open-04](/img/drivers/115/115-open-04-d.png#dark) + + ![115-open-04](/img/drivers/115/115-open-04-l.png#light) + +## 3. Adding 115 Network Disk to OpenList + +### 3.1. Configuration Instructions + +#### 3.1.1. Root Folder ID + +The default root directory ID is: `0` + +Open the 115 network disk official website, and when clicking to enter the folder to be set, click the number after `cid` in the URL. + +For example, + +The `root folder ID` of this folder is `249163533602609229`. + +### 3.2. Start Adding + +1. Open the management interface of OpenList and click `Storage` in the left menu. + +2. On the storage list page, click the `Add Storage` button in the upper right corner. + +3. Select the driver as `115 Open Platform`. + + ![115-open-05-d](/img/drivers/115/115-open-05-d.png#dark) + + ![115-open-05-l](/img/drivers/115/115-open-05-l.png#light) + +4. Enter the mounting path, such as: `115`. + +5. Enter the root folder ID obtained above in `Root Folder ID` (please refer to [3.1.1. Root Folder ID](#_3-1-1-root-folder-id)). + +6. Enter the obtained `Refresh Token` and `Access Token` in the refresh token (if not obtained, please refer to [2. Preparation for Access](#_2-preparation-for-access)). + - 115's token refresh mechanism does not require AppKey and has IP-based rate limiting, so it is implemented using [local logic](https://github.com/OpenListTeam/115-sdk-go). + +7. Click the `Add` button to complete adding the 115 network disk. + +### 3.3. Method for Refreshing Access Token + +```mermaid +sequenceDiagram + participant OpenList + participant 115 + OpenList->>115: Post refresh_token + 115->>OpenList: Get new access_token and refresh_token +``` + +## 4. Notes + +::: warning Handling Method for Token Leakage +If the Token is accidentally leaked, you can go to the 115 device login management to解除应用授权 (revoke application authorization). + +- 115 APP: [iOS, Android] versions need to be ≥ 35.11.0 +- 115 Web端: [https://115.com/?mode=device_manage](https://115.com/?mode=device_manage) + + When it expires, the following content will be prompted: + + ```json + failed get objs: failed to list objs: code: 40140116, message: no auth + ``` + +An account can obtain two `Refresh tokens` in the same application. After the third acquisition, the first obtained `Refresh token` will expire. Using the first `Refresh token` will prompt the above error. +::: + +## 5. Using Other APP IDs to Obtain Refresh Tokens (Not Yet Implemented) + +::: tip +Under development, the tutorial has not been updated yet. Stay tuned! +::: + +## 6. Mobile QR Code Authorization PKCE Mode (Not Yet Implemented) + +::: tip +Under development, the tutorial has not been updated yet. Stay tuned! +::: + +## 7. Default Download Method Used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/123.md b/pages/en/guide/drivers/123.md new file mode 100644 index 000000000..bf7ede4e9 --- /dev/null +++ b/pages/en/guide/drivers/123.md @@ -0,0 +1,154 @@ +--- +top: 695 +categories: + - guide + - drivers +--- + +# 123 Pan / Share / Link + + + +::: warning +123 Cloud Disk has implemented anti-leech measures for this **reverse-engineered driver**. To avoid unnecessary disputes, it is recommended that users choose the more standard 123 Open Platform driver. If you continue to use this driver, you need to enable a **local proxy** to prevent anti-leech detection. + +- [123 Open Platform](./123_open.md) + +::: + +## Personal + +Free user 1G traffic download, unlimited upload, multi-IP sharing is prohibited + +::: warning + +```json +failed get objs: failed to list objs:当前账号存在安全风险,请使用短信验证码或者微信进行登录。 +``` + +solution: + +- This is because the 123 cloud disk officially prohibits the mounting on strange devices, If you are mounted locally, `or` Windows Server server mount `to open the 123 cloud disk web page to log in or modify a password once + +::: + +https://www.123pan.com/ + +Just fill in the account password. + +### Username + +The mobile phone number used to log in + +### Password + +The password used to log in + +### Root folder file_id + +Enter the folder you want to mount, the last string of the official website URL, such as: + +![123](/img/drivers/123/123.png) + +### Recommendations + +- It seems that the 123 API has a limited number of loads each time, so if you load hundreds of files in a folder at one time, an error may be reported +- It is recommended to A useful approach is not to put too many sub-items in the same folder. + +## Link + +only adapted to the authentication function, the link also needs to be filled in by yourself, like [UrlTree](/en/guide/drivers/url_tree), fill in the direct link copied in 123 direct link. + +::: danger Please read this reminder carefully +Since 123 Cloud Disk Direct Link is a paid service with a storage quota, if you decide to add more storage, please ensure you set up passwords, metadata, and other protective measures to prevent malicious traffic. + +After configuring these settings, test them in incognito mode to confirm everything is working correctly. If you fail to implement adequate anti-theft measures and experience malicious traffic overload, you will be held responsible for the consequences. + +::: + +First open **`123 Cloud Disk Direct Link Management`**: **https://www.123pan.com/DirectLink** Set an authentication key in the lower right corner, and then turn on the authentication status switch + +![123](/img/drivers/123/123_link_1.png) + +- Open Settings 123 Cloud Disk Settings: **https://www.123pan.com/Setting** and find the account ID [`1` label in the picture below] +- How to activate direct link? (You need to activate membership to use it) + - Find the folder you want to open, right-click on `Enable Direct Link Space`, and after opening it, you will see a link icon in the folder [Tag `2` in the picture below] +- How to get a direct link to a file? [Tag `3` in the picture below] + - Enter the folder that has 'enabled direct link space', find the file you need to obtain, right-click 'Get direct link', and fill in the OpenList configuration after obtaining it. + ![123](/img/drivers/123/123_link_2.png) + +### Fill in example + +- **Origin urls**:Fill in the direct link to the files we copied one by one + - It supports creating different folders like [UrlTree](/en/guide/drivers/url_tree), and supports filling in the file size and modification time (four options are provided for use, like the UrlTree filling, refer to the second example of the picture below) + - File size unit: `B` bytes. For example, if you want to write a 1MB file, it is 1048567 bytes. You can search for the specific byte conversion in your browser (you can leave it blank). + - File modification time: It is `Unix timestamp`. You can search in the browser for the specific conversion (you can leave it blank). + + Fill in the format: + + ```txt + [FileSize:][Modified:]URL + 127451136:1694101621:https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_mips64el.deb + [FileSize:]URL + 134847488:https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_loong64.deb + [Modified:]URL + 1694101621:https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_arm64.AppImage + URL + https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_x86_64.AppImage + ``` + +- **Private key**: + - You can set up the direct link management page yourself and turn it on. Please turn it on. +- **Uid**: + - Account ID in the account settings page +- **Valid duration**: + - The file direct link validity period, in minutes, is filled in by default to 30 minutes + +![123Link](/img/drivers/123/123_link_3.png) + +![123Link](/img/drivers/123/123_link_4.png) + +## Share + +Fill in the driver's **`share key`** and optional **`share password`** (if there is a password, you need to fill in), the root folder ID defaults to `0` to display all files + +### Fill in the example + +![123](/img/drivers/123/123_add_demo.png) + +### Share Password + +Fill in if you have it, don't if you don't + +### Root folder file_id + +The ID of the root directory of the shared link is `0`, displaying all files + +If you only want to display a certain folder, open the developer mode (F12) to clear all requests (maybe 123 prohibits debug debugging, you need to close this by yourself to continue) + +Find the request on the right side of the picture in the upper-level directory request of the directory you want to display, then click `Response`, find the format button `{}` below and format it, and you can see the relevant directory ID. + +If you are not sure whether the directory ID is correct or not, there is a directory name under the directory ID + +![123](/img/drivers/123/123_fl_id.png) + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/123_open.md b/pages/en/guide/drivers/123_open.md new file mode 100644 index 000000000..eb2d378ba --- /dev/null +++ b/pages/en/guide/drivers/123_open.md @@ -0,0 +1,120 @@ +--- +top: 696 +categories: + - guide + - drivers +--- + +# 123 Open + +https://www.123pan.com/developer + + + +## 1. Developer Application + +::: warning +This driver uses the [developer authorization mode](https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/hpengmyg32blkbg8), which grants direct management access to the cloud drive associated with the provided public/private key pair, , so you **must use your client id and client secret**. + +The acquired token counts as a login device + +::: + +**Application Method**: Visit the [123 Open Platform Official Website](https://www.123pan.com/developer), read the Developer Agreement, fill in the required fields marked with `*`, and apply for the `client_id` and `client_secret`.Typically, after your application is approved the keys will be sent to your email **remember to check your spam folder, and please keep the keys sent by email safe**. + +1. Sign the Developer Agreement + +2. Fill out the application materials + +3. Wait for the review notification + +**Reference Tutorial**: [OpenListTeam/discussions#55](https://github.com/orgs/OpenListTeam/discussions/55) + +### 2. Get UID + +The method to obtain the "Cloud Drive UID" required during the application process is as follows: + +1. **Log in to the 123 Cloud Drive web platform** + + Visit the 123 Cloud Drive official website and log in with your account (phone number). + +2. **Go to the "Settings" page** + + After logging in, click on the profile picture or username at the top right, and select "Settings" (or directly visit: ). + +3. **Find the "Account ID"** + + In the "Account Settings" or "Security Settings" section, locate the "Account ID," which is your "Cloud Drive UID." Copy it and paste it into the application form. + +## 4. Add in OpenList + +### RefreshToken + +**keep it empty** + +### Client ID + +Enter your client ID + +### Client Secret + +Enter your client secret + +### Root Folder ID + +The default root directory ID is: `0` + +Open the official website of 123 Cloud Drive, navigate to the folder you want to set, and then click the number following `homeFilePath` in the URL. + +For example, + +API queries can also be used + +The `root folder ID` of this folder is `123456`. + +### Direct Link + +Disabled by default; returns standard download links. When enabled, returns CDN direct links, which require VIP access and will consume direct link traffic quota. + +Users must manually enable direct link space: Go to the 123 Cloud Drive official website, right-click a folder under the **root directory**, and select `Enable Direct Link Space (VIP)`. + +![](/img/drivers/123/123open-02.png) + +### Direct Link Private Key + +Prerequisite: Enable `Direct Link`. + +Leave empty to disable direct link authentication and return permanent direct links. + +To prevent your site resources from being maliciously downloaded or stolen, you can configure an "Authentication Key" in 123 Cloud Drive's **Direct Link** → **Basic Function Configuration** → **URL Authentication**, and then set **Authentication Status** to **Enabled**. + +After entering the key, the obtained direct links will automatically include authentication parameters. + +![](/img/drivers/123/123open-03.png) + +### Direct Link Valid Duration + +Prerequisite: Enable `Direct Link` and configure the `Direct Link Private Key`. + +Used to generate the expiration timestamp in the direct link authentication parameters. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[local proxy]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[302]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/139.md b/pages/en/guide/drivers/139.md new file mode 100644 index 000000000..aab2d4867 --- /dev/null +++ b/pages/en/guide/drivers/139.md @@ -0,0 +1,219 @@ +--- +top: 692 +categories: + - guide + - drivers +--- + +# 139Yun + + + +Cloud disk address: **** + +::: warning +The OpenList version must be greater than `v3.41.0` to use this tutorial. +::: + +::: tip +Most parameters can be obtained from browser DevTools. See [Search keywords](#search-keywords). +::: + +## Quick start + +For long-term use, use the new personal cloud with password login fallback. This lets OpenList persist the generated `Authorization` and renew it automatically when possible. + +1. Log in to in your browser. +2. Copy cookies from `mail.10086.cn` and paste them into `MailCookies` as a Cookie Header String, for example `key1=value1; key2=value2`. Do not paste JSON, a table, or one cookie per line. +3. Fill your 139 email/mobile account in `Username`, and fill the corresponding password in `Password`. +4. Add a `139Yun` storage in OpenList and fill: + - `Type`: `personal_new` + - `MailCookies`: the cookies copied in step 2 + - `Username`: your account + - `Password`: your password + - `Root folder ID`: leave empty or fill `/` for the root directory +5. Leave `Authorization`, `Cloud ID`, and `UserDomainID` empty. +6. Save the storage. + +If you only want to mount quickly, you can fill only `Authorization`: log in to , find a `hcy/file/list` request in DevTools -> Network, copy the request header `Authorization`, and paste only the content after `Basic `. + +If you want to mount a subfolder, enter that folder on the 139Yun website first, then use the current `parentFileId` or `currentCatalogID` as `Root folder ID`. + +## Authentication + +The driver supports three authentication methods. Use only one method unless you need password login as a fallback. + +| Method | Fields to fill | Notes | +| ----------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Password login fallback | `MailCookies`, `Username`, `Password` | Recommended for long-term use. `MailCookies` must be a Cookie Header String, such as `key1=value1; key2=value2`. The driver can generate and persist a new `Authorization`, and can fall back to password login when token refresh fails. | +| Authorization | `Authorization` | Fastest setup. Fill in the value after `Basic `. Do not include `Basic` itself. You may need to update it manually after it expires or refresh fails. | +| Mail cookies fast login | `MailCookies` | Use cookies from `mail.10086.cn`. `MailCookies` must be a Cookie Header String and contain valid key-value pairs; `Os_SSo_Sid` and `RMKEY` are used for fast login. | + +`Username`, `Password`, and `MailCookies` are not required when `Authorization` is valid, even if an old frontend marks them as required. + +When using password login fallback, `MailCookies`, `Username`, and `Password` must be filled together. + +`MailCookies` should look like the value of an HTTP `Cookie` request header: `key1=value1; key2=value2; key3=value3`. + +## Type + +OpenList currently supports five 139Yun storage types. `personal_new` is the default. + +| Type | Use for | Root folder ID when empty | Cloud ID | Notes | +| -------------- | ------------------------- | ------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------- | +| `personal_new` | New personal cloud | `/` | Not required | New API. Uses direct EOS multipart upload. | +| `family` | My Family -> Family Files | Automatically tries to save `data.path` without the `root:/` prefix | Required | Uses Family Cloud APIs. If auto-detection fails, fill the folder ID manually. | +| `group` | Shared group | Uses `Cloud ID` | Required | For groups created by others, manually fill the folder ID to avoid first-level folder loops. | +| `personal` | Old personal cloud | `root` | Not required | Legacy personal cloud. Most accounts have been migrated to `personal_new`. | +| `share` | Shared link mount | Share link ID from `LinkID` | Not required | Mount other users' shared content. Use `LinkID` to specify one or more share links. | + +::: warning +After changing `Type`, clear or update `Root folder ID`, then save the storage again. +::: + +## Root folder ID + +`Root folder ID` specifies the mounted directory. + +- `personal_new`: use `/` for the root. For a subfolder, use the folder ID from `parentFileId` or `currentCatalogID`. +- `family`: leave empty to let OpenList try to read `data.path` automatically. When filling manually, remove the `root:/` or `root:` prefix. +- `group`: leave empty only when mounting your own group root. For a subfolder or a group created by others, fill the folder ID manually. +- `personal`: use `root` for the legacy root. +- `share`: not used. The share link ID from `LinkID` replaces the root folder concept. + +Do not add extra `/` around subfolder IDs. For example, use `abc123`, not `/abc123`. + +## Cloud ID + +`Cloud ID` is required only for `family` and `group`. + +- `family`: family cloud ID. +- `group`: group ID. +- `personal_new` and `personal`: leave empty. +- `share`: leave empty. + +## User domain ID + +`UserDomainID` is the `ud_id` value in cookies. It is optional for mounting and is mainly used to show disk usage in storage details. If it is empty, file listing and downloads can still work, but capacity statistics are unavailable. + +## Advanced options + +- `Custom upload part size`: upload part size in bytes. `0` means automatic. The driver uses `100 MB` by default and increases it to `512 MB` for files larger than `30 GB`. +- `Report real size`: enabled by default. For old personal, family, and group uploads, it reports the real file size to the upstream API. +- `Use large thumbnail`: disabled by default. Enable it to prefer large image thumbnails when the new personal cloud API returns them. +- `Use old stream upload`: disabled by default. Enable it to use the legacy streaming upload method for family and group cloud types. The new method supports rapid upload (server-side duplicate check) but does not support streaming; the legacy method does not support rapid upload. + +## Proxy Range + +`Proxy Range` is enabled by default in the driver, but it only takes effect after enabling `Web Proxy` or `WebDAV Native Proxy`. + +Enable proxy mode when a player or downloader cannot handle the upstream 302 link correctly, for example when video playback fails, seeking fails, or resumable downloads do not work. + +## Search keywords + +Use browser DevTools to find the fields below. + +| Need | Where to search | Field | +| ------------------------- | --------------------------------------------------- | -------------------------------------------------------------- | +| `Authorization` | Network request headers on `yun.139.com` | `Authorization: Basic ...`; copy only the value after `Basic ` | +| New personal folder ID | `hcy/file/list` request, or browser storage | `parentFileId` or `currentCatalogID` | +| Family Cloud ID | `queryContentList` request payload | `cloudID` | +| Family folder ID | `queryContentList` response | `data.path`; remove `root:/` or `root:` when filling manually | +| Group ID | `queryGroupContentList` request payload | `groupID` | +| Group folder ID | `queryGroupContentList` request payload or response | `path` or folder ID | +| Legacy personal folder ID | `getDisk` request or response | `catalogID` | +| `UserDomainID` | Browser cookies | `ud_id` | +| Share link ID | Share page URL on `yun.139.com` | The path segment after `/share/` in the sharing URL | + +In Firefox, cookies and local storage can be easier to find in DevTools -> Storage. + +### Personal new + +Choose one of the following methods to find `Authorization` and folder ID. + +![](/img/drivers/139/new_personal.png) +![](/img/drivers/139/139_new_au.png) + +If you want the folder ID of a subfolder, enter that subfolder first and then inspect the new request or storage value. Otherwise, the old folder ID may still be displayed. + +### Personal cloud + +![](/img/drivers/139/other-personal.png) +![](/img/drivers/139/ch-personal.png) + +### Family cloud + +![](/img/drivers/139/other-family.png) +![](/img/drivers/139/ch-family.png) + +::: details Teaching video +Although the video is for V2, the method for obtaining folder ID and Cloud ID is still similar. + +**** +::: + +### OpenList fill in examples + +- `Authorization`: fill in only the content after `Basic `. +- New personal folder ID: enter the target folder first, then use the current `currentCatalogID`. + +![](/img/drivers/139/add-personal.png) +![](/img/drivers/139/add-family.png) +![](/img/drivers/139/add_new_personal.png) + +## Share mount + +The `share` type allows you to mount shared content from other 139Yun users. You need a valid authorization to access download APIs. + +### LinkID format + +### LinkID 格式 + +`LinkID` supports the following formats: + +- Single share: directly enter the share link ID +- Password-protected share: `link_id#password` +- Multiple shares: separate multiple entries with commas or new lines. For example: `share_a,share_b,share_c` or `share_a#pass1,share_b` + +### Get share link ID + +1. Open the share link in your browser, e.g. `https://yun.139.com/w/#/share/xxxxx` +2. The path segment after `/share/` is the share link ID (e.g. `xxxxx`) +3. If the share is password-protected, append `#password` after the link ID + +### Configuration + +- **Type**: `share` +- **LinkID**: one or more share link IDs (see format above) +- **Authorization** or **MailCookies + Username + Password**: required for authentication (same as other types) +- **Root folder ID**: not used for share type. The share link ID in `LinkID` determines the mounted content. +- **Cloud ID**: leave empty + +Notes: + +- Upload operations are not supported for the `share` type. +- When mounting multiple shares, the root directory shows each share as a separate folder. +- `${data_path}` resolves to the share root for path-based operations. + +## Download method + +The default download method is 302 redirection. If a player such as PotPlayer cannot play through 302, switch that mount to proxy mode or mount it through WebDAV with `WebDAV Native Proxy`. + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/163music.md b/pages/en/guide/drivers/163music.md new file mode 100644 index 000000000..f42673f64 --- /dev/null +++ b/pages/en/guide/drivers/163music.md @@ -0,0 +1,51 @@ +--- +top: 677 +categories: + - guide + - drivers +--- + +# 163 Music Drive + + + +163 Music Drive can only upload music and other operations on the mobile phone, and the web page cannot operate + +## Cookie + +Open **https://music.163.com** and then open the F12 developer mode + +Open a request at will, just carry the `Cookie` to find the following two parameters and fill in the OpenList` Cookie` + +- **\_\_csrf** 、**MUSIC_U** + ![](/img/drivers/163/163_cookie.png) + +## Song Limit + +You can only get 200 songs by default. If you exceed you, you can modify this option + +### Precautions + +1. If there are many list files, it is recommended to open pagination loading, otherwise it will not be loaded +2. Cloud space can only upload audio format files, and cannot upload other format files + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/189.md b/pages/en/guide/drivers/189.md new file mode 100644 index 000000000..830fa4e08 --- /dev/null +++ b/pages/en/guide/drivers/189.md @@ -0,0 +1,105 @@ +--- +top: 694 +categories: + - guide + - drivers +--- + +# 189Cloud + + + +::: tip +The web -side login has been replaced with sliding verification code, **no longer supports OCR or manual input**. If the verification code needs to be used, please use the add `Cookie` to log in + +If you encounter "Device ID does not exist, secondary device verification required",Open the 189 Account website at , log in, and then disable the Device Lock.. + +::: + +## 189CloudTV + +Uses the TV interface of 189 Cloud Drive, with the fewest mounting steps. + +(Some users have reported throttling issues. If you experience video playback stuttering, black screens, or failure to load, please switch to the 189 Cloud PC client.) + +1. When mounting, select 189CloudTV. Leave the login parameters blank. If you are unsure, **just fill in the mount path**. + +2. After clicking save, simply return to the storage management page. You can choose to log in by scanning a QR code or click the link to log in (for link login, if you are unsure, it is recommended to **right-click the link and open it in a new window**). + + Choose link or QR code + +3. After entering, select SMS login. Once logged in, return to the storage management page. Disable and re-enable the storage to use it normally (you may need to refresh the page). + + Select SMS login + +## Personal Cloud + +### Username + +the phone number used to log in + +### Password + +password for login + +### Root folder ID + +The string at the end of the official website url, such as: + +- https://cloud.189.cn/web/main/file/folder/-11 -> `-11` +- https://cloud.189.cn/web/main/file/folder/71398114617385472 -> `71398114617385472` + + ![189](/img/drivers/189/189.png) + +### Family transfer + +Give 189 Cloud adds Personal's `Family Transfer option`, which is convenient for users without VIP, and a large number of family cloud spaces upload. + +- Note: The old upload interface family cloud will still limit the upload quantity, so `Rapid upload` and ` Old Upload` will not take effect + +## Family Cloud + +(189 Cloud PC Driver Only) Use a computer browser, open the developer tool (F12), switch the emulation device and select the mobile device + +Open https://h5.cloud.189.cn/main.html#/family, enter the folder you want to mount, you can see the request in the network, and then find the required parameters: + +![189](/img/drivers/189/189-1.png) + +### OpenList fill in examples: + +#### 189 Cloud + +Fill in the account1and password2,Then click one request in the request, just bring `Cookies`3, click on one at will Then fill in,Cookie expires time is unknown + +![189](/img/drivers/189/189-0.png) + +#### 189 CloudPC + +![189](/img/drivers/189/189-2.png) + +Video reference: **https://www.bilibili.com/video/BV16A4y197De** + +## suggestion + +It is recommended to use the 189 Cloud PC first, [**Notes click to view.**](../../faq/howto.md#when-adding-a-189-cloud-storage-the-device-id-does-not-exist-and-a-secondary-device-verification-is-required) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/alias.md b/pages/en/guide/drivers/alias.md new file mode 100644 index 000000000..1e1eafee9 --- /dev/null +++ b/pages/en/guide/drivers/alias.md @@ -0,0 +1,10 @@ +--- +top: 997 +categories: + - guide + - drivers +--- + +# Alias + + diff --git a/pages/en/guide/drivers/alias_text.md b/pages/en/guide/drivers/alias_text.md new file mode 100644 index 000000000..abd6a59c8 --- /dev/null +++ b/pages/en/guide/drivers/alias_text.md @@ -0,0 +1,282 @@ +**Alias** is a feature that allows multiple different cloud drives or storage paths to be merged into a unified directory. By combining paths, content from different cloud drives or folders can be displayed in the same interface, simplifying access and management. + +For example: Cloud Drive Account 1 and Cloud Drive Account 2 both contain a folder named `Movies`, but the contents of these folders may not be identical. + +- **Previously (Virtual Path)**: + + You could only mount them to two different paths separately, like: + - `CloudDrive1/Movies`, `CloudDrive2/Movies` + - `Movies/CloudDrive1`, `Movies/CloudDrive2` + +- **Now (Alias)**: + + An aggregated folder (Movies) is provided, which can contain content from both Cloud Drive 1 and Cloud Drive 2 simultaneously. + +Folders with the same name will be automatically merged into one. The contents of the folder are the sum of the contents of all folders with the same name. Regarding how this driver handles files with the same name, please refer to the [Path conflict policies](/en/guide/drivers/alias#path-conflict-policies) section later. + +For Example: + +![alias](/img/drivers/alias/alias.png) + +In the diagram, we can see that two different folders are merged into one. Files and folders with the same name are also combined, and unique ones are displayed separately. + +Example explanations: + +- **Example 1**: `riluo.jpg` is unique to Cloud Drive 1, so it is shown separately. +- **Example 2**: Both folders contain a `video` folder, but the contents of these folders will be merged. Subfolders also follow the **same-name merging** display rule. Both folders contain two videos, but one of them has the same name. After merging according to the **same-name merging** rule, three video files will be displayed in total. + +## Paths filling method + +There are two ways to fill in: + +1. The first one is that you can only fill in the path of the subfolder and the folder with the same name must be used at the end. It is not recommended to use :x: + - Paths filling example: + ``` + /file1/locala + /file2/localb + ``` +2. The second is to directly mount the root folder path, using the `renaming` method, it is strongly recommended to use :heavy_check_mark: + - Paths filling example: + + ``` + #Example 1 Directly write the root folder + merge: /file1 + merge: /file2 + + #Example 2 Mount different path folders for merging + merge: /file1/localtest233 + merge: /file2/videos/TV series + merge: /file3 2/TV/Domestic TV Series/Station XX + ``` + +According to the second method, we can `merge` and display different folders, which is convenient and quick. + +## Path conflict policies + +The three configuration items—**Reading conflict policy**, **Writing conflict policy**, and **Putting conflict policy**—determine how the Alias handles files or folders with the same name and identical paths in the backend drivers. Their values and corresponding behaviors are as follows: + +### Reading conflict policy + +Determines the behavior for handling files with the same name when downloading, copying (as source files), and extracting (as source files) (excluding moving). + +- Get the file corresponding to the first conflict path: Select the file from the first available path (from top to bottom in the Paths) where the file exists. + +- Load balancing on a per-file basis: Randomly select one file from the available duplicates. + +- Load balancing on a per-part basis: + - During 302 downloads, copying, or extracting, it falls back to the **Load balancing on a per-file basis** strategy. + - When using local proxy downloads, each transmitted chunk is randomly assigned to one of the duplicate files. This requires the backend driver to support Range requests. For details on setting the chunk size, refer to the section [Download concurrency, Download part size](/en/guide/drivers/alias#download-concurrency-download-part-size). + - This policy achieves the actual effect as shown in the figure. It is important to note that this diagram is only intended to conveniently demonstrate the operation of this policy. The statement that "participating load-balanced files with the same name have different contents" is **not** a correct usage of this driver. OpenList does not guarantee stable or correct results under such circumstances, nor will it address any issues arising from this usage scenario. Furthermore, the actual minimum size for file splitting is 1 KiB, not 1 byte, so the effect depicted in the diagram will not occur in the stable version. + + ![](/img/drivers/alias/read_policy_all.png) + +::: tip +When a copy operation involves multiple source paths and target paths, the Alias will first attempt to pair source and target paths that belong to the same drive. Only for target paths that cannot be matched in this way will the method specified by the **Reading conflict policy** be used to select a source path. + +For example, when copying: + +- `DriverA/source/file.txt` +- `DriverB/source/file.txt` + +to: + +- `DriverB/target/` +- `DriverA/target/` +- `DriverC/target/` + +The Alias driver will perform the following operations: + +1. Copy `DriverA/source/file.txt` to `DriverA/target/`, since both are located in Driver A. +2. Copy `DriverB/source/file.txt` to `DriverB/target/`, since both are located in Driver B. +3. For `DriverC/target/`, which has no matching source in the same driver, select `file.txt` from either Driver A or Driver B according to the **Reading conflict policy**, and copy it to `DriverC/target/` via an upload operation (creating a copy task). + +::: + +::: tip +The move operation involves a matching process similar to that of the copy operation. For target paths that cannot be matched with a source path from the same driver, the Alias will randomly select from the still-unmatched source paths to create a pairing (**one-to-one correspondence**). If the number of source paths exceeds the number of target paths, the unmatched source paths will be deleted. If the number of source paths is less than the number of target paths, the move operation will fail. The matching behavior for the move operation is not affected by the **Reading conflict policy**. + +For example, in the case described for the copy operation, since there is no source path corresponding to `DriverC/target/`, the move operation will fail. + +As another example, when moving: + +- `DriverA/source/file.txt` +- `DriverB/source/file.txt` +- `DriverD/source/file.txt` +- `DriverE/source/file.txt` +- `DriverG/source/file.txt` + +to: + +- `DriverA/target/` +- `DriverB/target/` +- `DriverC/target/` +- `DriverF/target/` + +The Alias will perform the following operations: + +1. Move `DriverA/source/file.txt` to `DriverA/target/`. +2. Move `DriverB/source/file.txt` to `DriverB/target/`. +3. Randomly select one file from Driver D, E, or G, and move it to `DriverC/target/` (by creating a move task, which is essentially an upload followed by deletion). Let's assume E is selected. +4. Randomly select one file from the remaining unmatched drivers (D or G), and move it to `DriverF/target/`. Let's assume G is selected. +5. Delete `DriverD/source/file.txt`. + +::: + +### Writing conflict policy + +Determines the behavior for renaming or deleting files/folders with the same name, as well as creating folders within identically named folders. + +- **Disable writing**: Prohibits rename, delete, and folder creation operations. +- **Write into the first conflict path**: Operates on the driver containing the target path that appears first (top to bottom) in the configured path list. +- **Allow unique path**: Executes the operation only if the target path is unique (exists in only one backend path). Otherwise, the operation is prohibited. +- **Allow full conflict paths**: Executes the operation only if the target path exists in **all** configured backend paths, applying the operation across all of them. Otherwise, the operation is prohibited. +- **Allow unique path and full conflict paths**: Allows the operation when the target path is either unique or exists in all configured backend paths. +- **Write into all conflict paths**: Forwards the operation to all backend paths where the target path exists. + +::: tip +If the above explanation is unclear, you can refer to the following example. + +- In Driver A, the file exists: `file1.txt` +- In Driver B, the files exist: `file1.txt`, `file2.txt`, `file3.txt` +- In Driver C, the files exist: `file1.txt`, `file3.txt` + +Configured backend paths: + +``` +test:DriverA +test:DriverB +test:DriverC +``` + +Then: + +- Since the sub-path `/file1.txt` is valid in all configured paths (`DriverA/`, `DriverB/`, `DriverC/`), `/file1.txt` is referred to as a **full conflict path**. +- Since the sub-path `/file2.txt` is valid only under the single backend path `DriverB/`, `/file2.txt` is referred to as a **non-conflict path** or **unique path**. +- Since the sub-path `/file3.txt` exists under both `DriverB/` and `DriverC/`, and the number of backend paths where it exists is neither 1 nor the maximum (3), `/file3.txt` is neither a non-conflict path nor a full conflict path. + +When renaming any file to `file4.txt`, the corresponding files in the following drivers will be renamed: + +| Writing conflict policy | file1.txt | file2.txt | file3.txt | +| ----------------------------------------- | ---------------------------- | --------- | ------------------ | +| Disable writing | Fails | Fails | Fails | +| Write into the first conflict path | Driver A | Driver B | Driver B | +| Allow unique path | Fails | Driver B | Fails | +| Allow full conflict paths | Driver A, Driver B, Driver C | Fails | Fails | +| Allow unique path and full conflict paths | Driver A, Driver B, Driver C | Driver B | Fails | +| Write into all conflict paths | Driver A, Driver B, Driver C | Driver B | Driver B, Driver C | + +::: + +### Putting conflict policy + +Determines the behavior for uploading to, copying to, moving to, or extracting to folders with the same name. + +- **Disable putting**, **Put into the first conflict path**, **Allow unique path**, **Allow full conflict paths**, **Allow unique path and full conflict paths**, **Put into all conflict paths**: These options function identically to their counterparts in the **Writing conflict policy**. +- **Random load balancing**: Randomly selects one valid path for the upload. +- **Weighted random load balancing based on remaining space**: Retrieves the remaining free space of all valid paths, skips paths that fail to report free space or have insufficient space for the file being uploaded, and then randomly selects from the remaining valid paths, weighting the choice by their remaining capacity. If none of the valid paths that successfully reported free space have enough capacity for the file, **a random selection is made from among the valid paths that failed to report capacity**. +- **Strict weighted random load balancing based on remaining space**: Retrieves the remaining free space of all valid paths, skips paths that fail to report free space or have insufficient space for the file being uploaded, and then randomly selects from the remaining valid paths, weighting the choice by their remaining capacity. If none of the valid paths that successfully reported free space have enough capacity for the file, **an error message is returned directly**. + +::: tip +The load balancing mechanisms within the **Reading conflict policy** and the **Putting conflict policy** are two largely unrelated features. Informally speaking, the load balancing in the **Reading conflict policy** is analogous to RAID 1, while the load balancing in the **Putting conflict policy** is analogous to RAID 0. For specific use cases of each, please refer to [Advanced / Load balancing](/en/guide/advanced/balance). + +If you've understood the above, you'll realize that enabling both **Reading Load Balancing** and **Putting Load Balancing** won't make load balancing more balanced. In fact, this configuration produces effects that are hardly any different from enabling only **Putting Load Balancing**. + +::: + +::: tip +The legacy version of the Alias used three Boolean configuration items—**Writable**, **Protect same name**, and **Parallel write**—to implement path conflict policy functionality. The correspondence between the legacy configuration and the new configuration is as follows: + +- In the legacy configuration, the **Reading conflict policy** was always set to **Get the file corresponding to the first conflict path**. +- In the legacy configuration, when **Writable** was disabled, both the **Writing conflict policy** and the **Putting conflict policy** were set to **Disabled**. +- When **Writable** was enabled, the **Writing conflict policy** and the **Putting conflict policy** were both determined as follows: + | | Parallel write disabled | Parallel write enabled | + | -------------------------- | -------------------------------------- | ----------------------------------------- | + | Protect same name enabled | Allow unique path | Allow unique path and full conflict paths | + | Protect same name disabled | Write/Put into the first conflict path | Write/Put into all conflict paths | + +::: + +## File consistency check + +When enabled, the driver will filter out paths where the **size or hash value** differs from other copies during the process of collecting valid paths. This is a safety measure, and whether it is enabled has relatively minor impact. It is recommended to enable this option when using the **Load balancing on a per-part basis** reading policy. + +Regardless of whether this option is enabled, the Alias does not actively calculate file hashes. Instead, it performs a best-effort match using the hash values returned by the backend drivers. + +Different types of hash values returned by backend drivers will not lead to misjudgment. For example, if Driver 1 returns the MD5 of a file and Driver 2 returns the SHA1 of the file, even with this option enabled, the Alias will not consider either path from Driver 1 or Driver 2 invalid simply because the file's MD5 and SHA1 are not equal. + +## The download method to use + +When adding **`alias`**, `Web Proxy` and `Webdav Policy` are not modified by default. The storage path filled in the Paths path can be `302`, `Local Proxy`, `Download Proxy URL`, three modes Mixed Playback Mixed Playback is possible. + +If you checked `Web Proxy`, the storage filled in by the Paths path, if there is a 302 mode playback, it will be played in transit (local proxy mode) at that time, and it will become a proxy mode. If the Webdav policy is also changed, it will also change. + +Of course, it is up to you to choose whether to change the mode. + +### What if you don’t know how the cloud disks you added are different? + +1. You can go to the bottom of the corresponding document to view the document, there is a flow chart description + - If there is a 302, the 302 method is used by default. If there are only local proxy and download proxy URLs, the default is to use the local proxy, provided that you have not manually selected +2. You can check when adding storage, select the corresponding storage to view, for example, let’s check the methods of Alibaba Cloud and 115 respectively + - As you can see from the figure below, Alibaba Cloud Disk has the option of `web proxy`, and `webdav policy` defaults to 302. It can be judged that Alibaba Cloud Disk uses the 302 method by default + - As you can see from the figure below, the 115 network disk does not have the option of `web proxy`, and the `webdav strategy` defaults to the local proxy. It can be judged that the 115 network disk uses the local proxy mode by default + ![alias](/img/drivers/alias/alias-2.png) + +## Proxy Range + +You need to enable `Web Proxy` or` Webdav Native Proxy` to take effect. Currently only applicable to:`alias`、`139Yun`、`OpenList V3`. + +- The `139Yun` driver, when this option is enabled, can resolve issues that occur when a proxy is enabled but the download link does not return the correct HTTP status code, such as problems with video playback or lack of support for resume downloads. +- The `Alias` driver is added to meet specific use cases, for example, when `139Yun` uses a 302 redirect. By enabling the `Alias` proxy, downloads can use `139Yun` with the 302 redirect, while video playback can use the proxy-enabled `Alias`, reducing unnecessary load. +- The `OpenList` driver is added to support server-side OpenList mounting with `139Yun` using a 302 redirect. Locally, OpenList can be mounted via the proxy-enabled `OpenList` to access the server's OpenList for video playback, etc., to avoid consuming server bandwidth. This also allows for data-free video streaming on mobile networks. + +## Download concurrency, Download part size + +**Storage Settings:** + +- Alias (Alias) Drive +- Local Proxy +- Path: / Cloud Drive Mount Path +- Download Concurrency: 10 +- Download Chunk Size: 1024 + +**Effect:** + +- Client → OpenList Alias Drive: Uses 1 connection +- OpenList Alias Drive → Cloud Drive: Supports 10 concurrent connections, with actual concurrency limited by the cloud drive. + +```mermaid +graph LR + A[Client] -->|Connection 1| B[OpenList Alias Drive] + B -->|Concurrent Connection 1| C[Cloud Drive] + B -->|Concurrent Connection 2| C + B -->|Concurrent Connection 3| C + B -->|Concurrent Connection 4| C + B -->|Concurrent Connection 5| C + B -->|Concurrent Connection 6| C + B -->|Concurrent Connection 7| C + B -->|Concurrent Connection 8| C + B -->|Concurrent Connection 9| C + B -->|Concurrent Connection 10| C +``` + +- Single-threaded speed is slower, but it supports concurrency: Using the alias drive allows concurrent downloads, significantly improving speed. +- Video watching and download acceleration: The experience is enhanced by increasing concurrency. +- Copying from alias drive to other drives: File transfer is also accelerated in this case. + +**Friendly Reminder:** Please do not abuse this feature. Excessive use may cause abnormal activity on the cloud drive account, and you will bear the consequences. + +**Configuration Options:** + +- max_concurrency: Sets the maximum concurrency for the local proxy. The default is 64, and setting it to 0 means no limit on concurrency. + +## Other instructions + +If you are using `Windows`, the following situation will occur, and folders with different capitalization will also be regenerated. + +For example, Local 1 and Local 2 have a lowercase v for `video` respectively, and the folder OneDrive has an uppercase V `Video` folder. + +Then a lowercase video folder will be generated, which contains only `local 1, local 2` files merged by two folders. + +At the same time, the uppercase `Video` will gather the files of the three folders. + +This is because Windows is case-insensitive, video and Video will be considered as the same folder, you will not have this problem if you switch to Linux or Mac. diff --git a/pages/en/guide/drivers/alidoc.md b/pages/en/guide/drivers/alidoc.md new file mode 100644 index 000000000..a8abe5bbf --- /dev/null +++ b/pages/en/guide/drivers/alidoc.md @@ -0,0 +1,78 @@ +--- +top: 677 +categories: + - guide + - drivers +--- + +# AliDoc + +::: danger Please read the notes carefully +This driver is currently not officially maintained by the project team. +::: + +Mount DingTalk Docs web storage in OpenList. + +Official website: + +- DingTalk Docs: + +This driver uses DingTalk Docs web APIs captured from the browser, not the official open platform API. + +::: warning Stability Notice + +Because this driver depends on web-side APIs and Cookie authentication, it may fail when DingTalk Docs changes its frontend behavior, request format, or login flow. + +Please use it with that risk in mind. + +::: + +## Supported operations + +Currently supported: + +- List files and folders +- Download files +- Upload files +- Create folders +- Move files and folders +- Copy files and folders +- Rename files and folders +- Recycle files and folders + +## Cookie + +Required. DingTalk Docs web Cookie. + +Recommended steps: + +1. Open a fresh browser session or incognito window. +2. Visit and log in to the account you want to mount. +3. Press `F12` to open developer tools. +4. Open the `Network` tab and refresh the page. +5. Search for requests such as `list`, `createfolder`, or other `/box/api/` requests. +6. Open any one of these requests and find the `Cookie` request header. +7. Copy the complete Cookie value into OpenList. + +::: warning +Please avoid mixing multiple DingTalk accounts in the same browser environment when obtaining the Cookie. +::: + +## Root folder ID + +Required. This is the UUID of the root folder entity used as the mount root. + +You can obtain it from DingTalk Docs web requests: + +1. Stay on the folder you want to mount as root. +2. Open developer tools and inspect a `/box/api/v2/dentry/list` request. +3. Find the `dentryUuid` request parameter. +4. Use that UUID as `Root folder ID`. + +Usually, the personal root folder UUID is also returned in responses such as `spaceProfile.rootDentryUuid`. + +## Notes + +- This driver depends on Cookie login state. If the Cookie expires, you need to refresh it manually. +- Upload uses DingTalk Docs web upload flow, including OSS upload and final commit request. +- Delete currently means moving the file or folder to the recycle bin, not permanent deletion. diff --git a/pages/en/guide/drivers/aliyundrive_open.md b/pages/en/guide/drivers/aliyundrive_open.md new file mode 100644 index 000000000..f177b1c18 --- /dev/null +++ b/pages/en/guide/drivers/aliyundrive_open.md @@ -0,0 +1,287 @@ +--- +top: 699 +categories: + - guide + - drivers +--- + +# Aliyundrive Open + +::: tip +Aliyundrive Open, developed based on the official authorization API. +::: + +::: danger Read the precautions carefully + +1. The refresh token obtained by this tool can only be used for **Aliyundrive open platform** storage mount method. +2. The token filled in when OpenList is mounted should also be provided by this tool, and the previous acquisition method will not be available +3. Not applicable to others, but only for OpenList +4. If the same IP requests max times within minutes, **Too Many Requests** will appear. :no_entry_sign:So please do not abuse:no_entry_sign: + - viewing files and watching video downloads are not counted. The number of times and time mentioned above are dynamic~ + - New optimization: **will only refresh the token when the request finds that the token is expired, update restart editing if the token is still valid will not refresh.** +5. Online playback video reminder: **`ExceedCapacityForbidden`** error, the capacity exceeds restrictions, needs to be expanded or delete unnecessary files to save space,[View details](#four、) +6. **Do not share the Alibaba cloud disk publicly, and the account is prohibited from being accessed by multiple IPs. If the account is frozen after sharing, you will be responsible for the consequences. After adding storage, please set up meta information and other measures in time to prevent the account from being frozen by {other people/crawlers} malicious [access/sharing],Other cloud disks also need to be prevented from being frozen/banned.** +7. It is prohibited to use Alibaba Cloud Disk to build distribution services including but not limited to **`picture bed, software bed`** and **`video external link to video website playback`**, as well as some non-standard usage methods not mentioned above. If you violate the rules and are `frozen/banned`, you will be responsible for the consequences and it cannot be lifted. + +::: + +## 1. Necessary Conditions + +A **AliyunDrive** account is required. + +::: warning Notes +Speed and stability are related to the performance/network of the machine running OpenList and the load on the AliYun Drive server. +::: + +## 2. Preparation for Access + +### 2.1. Register an Application on the Open Platform (Optional; no need to create one if using the built-in key pair of OpenList/public welfare server/self-hosted server) + +::: tip +Please register an application according to the requirements of the AliYun Drive Open Platform. +::: + +Open Platform URL: + +### 2.2. Obtain Tokens + +1. Visit [api.oplist.org](https://api.oplist.org) **⚠️ If using a public welfare server/self-hosted server, visit the address of the public welfare server/self-hosted server.** + +2. Select **AliYun Drive App Login** from the drop-down menu. + + ![aliyun-open-01-d](/img/drivers/aliyun/aliyun-open-01-d.png#dark) + + ![aliyun-open-01-l](/img/drivers/aliyun/aliyun-open-01-l.png#light) + +3. If you are using the built-in key pair of `OpenList (or public welfare server/self-hosted server)` (i.e., you do not have application information from the AliYun Drive Open Platform), configure according to `3.1`, `3.2`, and `3.3`: + + 3.1. Check `Use parameters provided by OpenList`. + + 3.2. Leave both `Client ID (ClientID/AppID)` and `App Secret (AppKey/Secret)` blank. + + 3.3. Click the `Get Token` button. + + ![aliyun-open-02-01-d](/img/drivers/aliyun/aliyun-open-02-01-d.png#dark) + + ![aliyun-open-02-01-l](/img/drivers/aliyun/aliyun-open-02-01-l.png#light) + +4. If you are using your own created OAuth client ID and secret, configure according to `4.1`, `4.2`, and `4.3`: + + 4.1. Do not check `Use parameters provided by OpenList`. + + 4.2. Enter your `AppId` in `Client ID` and your `AppSecret` in `App Secret`. + + 4.3. Click the `Get Token` button. + + ![aliyun-open-02-02-d](/img/drivers/aliyun/aliyun-open-02-02-d.png#dark) + + ![aliyun-open-02-02-l](/img/drivers/aliyun/aliyun-open-02-02-l.png#light) + +5. In the pop-up window, scan the code to log in to your AliYun Drive. + + ![aliyun-open-03](/img/drivers/aliyun/aliyun-open-03.png) + +6. After successful authorization, the page will display your `Access Token` and `Refresh Token`. Please copy and save these two tokens. + + ![aliyun-open-04](/img/drivers/aliyun/aliyun-open-04-d.png#dark) + + ![aliyun-open-04](/img/drivers/aliyun/aliyun-open-04-l.png#light) + +## 3. Add AliYun Drive to OpenList + +### 3.1. Start Adding + +1. Open the OpenList management interface and click `Storage` in the left menu. + +2. On the storage list page, click the `Add Storage` button in the upper right corner. + +3. Select the driver as `AliYun Drive (Oauth2)`. + +4. Enter the mount path, such as `aliyun-drive`. + +5. Enter the root folder ID obtained above in `Root Folder ID`. If using the root directory, enter `root`. See [Configuration Instructions/Root Folder ID](#_3-2-1-root-folder-id). + + ![aliyun-open-05-l](/img/drivers/aliyun/aliyun-open-05-l.png#light) + + ![aliyun-open-05-d](/img/drivers/aliyun/aliyun-open-05-d.png#dark) + +6. Enter the refresh token obtained above in the refresh token field (if not obtained, refer to [Preparation for Access](#_2-preparation-for-access)). + +7. If you are using the OAuth client ID and secret provided by `OpenList (or public welfare server/self-hosted server)`, configure according to `6.1`, `6.2`, and `6.3`: + + 6.1. Check `Use online api`. + + 6.2. Leave both `Client ID (ClientID/AppID)` and `App Secret (AppKey/Secret)` blank. + + 6.3. Enter `https://api.oplist.org/alicloud/renewapi` in `Api url address`. If using a `public welfare server/self-hosted server`, enter the corresponding server address. + + ![aliyun-open-06-l](/img/drivers/aliyun/aliyun-open-06-l.png#light) + + ![aliyun-open-06-d](/img/drivers/aliyun/aliyun-open-06-d.png#dark) + +8. If you are using your own created OAuth client ID and secret, configure according to `7.1`, `7.2`, and `7.3`: + + 7.1. Do not check `Use online api`. + + 7.2. Enter your `AppId` in `Client ID (ClientID/AppID)` and your `AppSecret` in `App Secret (AppKey/Secret)`. See [Configuration Instructions/Client ID, Secret](#_3-2-2-client-id-secret). + + 7.3. Leave `Api url address` blank. + + ![aliyun-open-07-l](/img/drivers/aliyun/aliyun-open-07-l.png#light) + + ![aliyun-open-07-d](/img/drivers/aliyun/aliyun-open-07-d.png#dark) + +9. In the `Instant Upload` option, select whether to enable the instant upload function (optional). See [Configuration Instructions/Instant Upload](#_3-2-3-instant-upload). + +10. In `Removal Method`, select the way to delete files (optional), default is recycle bin. See [Configuration Instructions/Removal Method](#_3-2-4-removal-method). + +11. In `Cloud Drive Type`, select the cloud drive type (optional), default is `Default`. See [Configuration Instructions/Cloud Drive Type](#_3-2-5-cloud-drive-type). + +12. In `Livp Download Format`, select the format for downloading live photos (optional), default is `Jpeg`. See [Configuration Instructions/Livp Download Format](#_3-2-6-livp-download-format). + +13. In `Internal Upload`, select whether to enable internal upload (optional), default is off. See [Configuration Instructions/Internal Upload](#_3-2-7-internal-upload). + +14. Click the `Add` button to complete adding AliYun Drive. + +### 3.2. Configuration Instructions + +#### 3.2.1. Root Folder ID + +The default is `root`, displaying all cloud drive contents. If you only want to display contents within a certain folder, change it to `file_id`. + +Open the AliYun Drive official website, and when clicking into the folder to be set, click the string after the URL. + +For example, https://www.alipan.com/drive/folder/5fe01e1830601baf774e4827a9fb8fb2b5bf7940 + +The file_id of this folder is `5fe01e1830601baf774e4827a9fb8fb2b5bf7940`. + +![file_id](/img/drivers/aliyundrive.png) + +#### 3.2.2. Client ID, Secret + +Normal users can leave it blank. If you have applied for official authorization, you can fill in your own. When blank, it defaults to using those provided by OpenList. + +- If using your own developer account, the Oauth token link must also be modified, and you must use your own developer account ID and secret to obtain the refresh token. + +#### 3.2.3. Instant Upload + +- **Version v.3.22.1** new feature. + + Instant upload requires enabling `Instant Upload` when mounting. The prerequisite for instant upload is that the file already exists in AliYun Drive; otherwise, it will be a normal upload. + + Instant upload calculates the file's `sha1` value (should be correct) without requiring you to calculate it. You just need to upload, and it will automatically calculate after upload completion. + +- It uses the interface provided by AliYun Drive's official API. For details, see :point_right: [**Instant Upload Logic**](https://www.yuque.com/aliyundrive/zpfszx/ezlzok#y7lyH). + +--- + +- **Version v.3.27.0** enhances instant upload: files can be instantly transferred between `115 Network Disk` and AliYun Drive by direct copying. + - Prerequisite: To instantly transfer a file from AliYun Drive to 115, the file must already exist in 115; otherwise, it will be a normal copy task. + - If instantly transferring a file from 115 to AliYun Drive, enable the instant upload option for AliYun Drive; otherwise, it will be a normal upload. + It is recommended to locally deploy an OpenList on your home computer, add a `local storage` and `AliYun Drive`, and use copy instant upload to save resources. + +#### 3.2.4. Removal Method + +If not set, it defaults to deleting to the recycle bin. If space is insufficient, you need to manually clean the recycle bin. If a file is deleted by mistake, please go to the AliYun Drive official website client to retrieve it. + +- **Recycle Bin**: Deleting in OpenList will move the file to the network disk recycle bin, occupying cloud drive space, but it can be retrieved later if deleted by mistake. +- **Delete**: Direct deletion without staying in the recycle bin, not occupying cloud drive space, but cannot be retrieved later if deleted by mistake. + - Note: Do not leak the Token obtained from AliYun Drive Open. If leaked by mistake, please immediately: + - **Open AliYun Drive app → My → Settings gear icon in the upper right corner → Privacy Settings → Authorization Management → Click OpenList to view and revoke authorization**. + - Then rescan the code to authorize for a new refresh token. Revoking authorization will invalidate all previously obtained tokens to protect your account security. + +#### 3.2.5. Cloud Drive Type + +You can select the cloud drive type according to your needs. When adding a driver using OpenList version v3.25.1 and above, the default selection is `Default`. + +- **Default**: For users with a talent level, it defaults to displaying `non-backup file` content; for users without a talent level, it defaults to displaying `backup file` content. +- **Resource Library**: Corresponding to the `non-backup file content` of the cloud drive. +- **Backup Disk**: Corresponding to the `backup files` of the cloud drive. + After AliYun Drive updated to version `6.0` in July 2024, the backup disk was changed to merge backup files and all files, but in terms of OpenAPI, it still distinguishes between `Resource Library` and `Backup Disk` types. + ![](/img/drivers/aliyun/aliyun_type.png) + +#### 3.2.6. Livp Download Format + +That is, iOS `live` photos cannot be parsed by non-iOS systems, so they cannot be downloaded. Now, you can choose `Jpeg` or `Mov` according to your needs. + +- Jpeg: The cover of the `live` photo. +- Mov: The video frame of the `live` photo when long-pressed. + +#### 3.2.7. Internal Upload + +If the server where OpenList is deployed is an AliYun ECS in the Beijing region, turning on this switch can improve file upload speed. Do not turn on this switch for servers that do not meet the requirements, otherwise, upload failures will occur. + +- Can `Internal Upload` be used on non-Beijing AliYun ECS? No, because AliYun Drive uses object storage in the Beijing region. + +## 4. Other Instructions + +### 4.1. Permission Scope + +OpenList only obtains cloud drive user (**name, avatar ¹**), (**file access permission ²**), and (**file write permission ³**), without obtaining phone number permission. + +Details can be viewed in the following two ways: + +1. The **prompt** when scanning the code to obtain the refresh token. +2. Open **AliYun Drive app → My → Settings gear icon in the upper right corner → Privacy Settings → Authorization Management → Click OpenList** to view. + - You can manually revoke permissions at any time if not in use. + +### 4.2. About Video Playback + +The default player is AliYun Drive's built-in player - **Aliyun Video Previewer**. +Videos uploaded to the cloud drive will be transcoded into H.264-encoded video streams through the transcoding service. (The benefit is that iOS can play them... Well, it should be like this.) + +- For newly uploaded videos, only the first 30s of the video is pre-transcoded by default. The first play will trigger full video transcoding. The transcoded video will be cached, and no retranscoding is needed for subsequent plays. +- Videos shorter than 30s will not trigger pre-transcoding, and transcoding will only start when played for the first time. +- Real-time transcoding takes some time. +- Video qualities are: LD|SD|HD|FHD|QHD. + +### 4.3. Inability to View Office Suite Files + +> Q: Why can't Office suite files be viewed in AliYun Drive Open? +> +> A: ~~Because AliYun Drive has not opened the relevant API, so it cannot be viewed temporarily~~ (Stay tuned.) + +### 4.4. Subtitle Loading + +Q: How to load subtitles? + +A: AliYun Drive Open needs to use the **Aliyun Video Previewer** player, and currently only supports transcoding srt and vtt text format subtitles encapsulated in mkv. + +- Currently, only three languages are supported: eng, jpn, chi. Other languages will be lost. + +### 4.5. Common Issues + +1. ExceedCapacityForbidden + + Q: When playing AliYun Drive videos online, it cannot play and prompts the error: **`ExceedCapacityForbidden`**. + + A: **The user's capacity is exceeded**, restricting playback. You need to expand the capacity or delete unnecessary files to free up space + - In OpenList, it prompts: `ExceedCapacity Forbidden: Execeed Capactity Forbidden`. + - In AliYun Drive, it prompts: `云盘可用空间不足` (Insufficient available space in cloud drive). + +2. Transcoding in progress, please try again later. + + Q: When viewing a video in AliYun Drive, it prompts `Transcoding in progress, please try again later`, and when mounting in OpenList, it prompts: `failed link: failed get link: UserNotAllowedAccessResource: user not allowed access resource for violation`. + + A: It means the account has been frozen (previously, it was directly unable to log in). For specific situations, please check the red content notes at the top. + +## 5. The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/autoindex.md b/pages/en/guide/drivers/autoindex.md new file mode 100644 index 000000000..73c115381 --- /dev/null +++ b/pages/en/guide/drivers/autoindex.md @@ -0,0 +1,79 @@ +--- +top: 350 +categories: + - guide + - drivers +--- + +# AutoIndex + +The AutoIndex driver is used to mount directory index pages built into HTTP servers, such as the [Nginx Source Index](https://nginx.org/download/) and [Redis Releases](https://download.redis.io/releases/). + +This driver essentially scrapes page information directly and requires manually specifying XPaths, making it suitable for scraping pages that are not directory index pages as well. + +## Setup Instructions + +- **URL:** The website address. If no scheme is included, `https://` will be automatically added. To mount a subpath, append it directly to the end of the URL. For example, can be used to mount the subpath `tomcat/` under . Required. +- **Entry XPath:** An XPath expression used to match each entry node in the file list. The result of this expression must be a **node-set**, where each node corresponds to a file entry. Required. +- **Filename XPath:** Within the context of each node matched by **Entry XPath**, this XPath expression is used to extract the filename. The result can be a node, node-set, or string. If the result is a node or node-set, the innerText of the first node will be used as the filename. Required. +- **Modification time XPath:** Within the context of each node matched by **Entry XPath**, this XPath expression is used to extract the file modification time. The result can be a node, node-set, or string. If the result is a node or node-set, the innerText of the first node will be used as the modification time string. Optional. If left blank, no node is matched, or the date format is unrecognizable, the current time will be used by default. +- **File size XPath:** Within the context of each node matched by **Entry XPath**, this XPath expression is used to extract the file size. The result can be a node, node-set, number, or string. If the result is a node or node-set, the innerText of the first node will be parsed as the file size. Optional. If left blank, no node is matched, or the content cannot be recognized as a valid size, the driver will return a file size of 0. +- **Ignore filenames:** Used to filter out entries that should not be crawled (e.g., list headers, parent directory links). Enter the filenames to ignore (without the trailing `/`). +- **Modification date format:** Enter a time string matching the format displayed on the page (refer to the Go time template: `Mon Jan 2 15:04:05 -0700 MST 2006`, see also: [Go Time Formatting Documentation](https://golang.org/pkg/time/#pkg-constants)). + +## Reference Configuration + +The following configurations can be used to mount the auto-index pages of some HTTP servers. Since the style of auto-index pages may change with updates to the HTTP server version, the configurations provided below are for reference only and are not guaranteed to work across all versions. You are welcome to supplement available configurations for other HTTP servers in the comments. + +#### Nginx (Tested on 1.29.0) + +- Entry XPath: `//pre/a` +- Filename XPath: `.` +- Modification time XPath: `substring(normalize-space(./following-sibling::text()[1]),1,17)` +- File size XPath: `substring(normalize-space(./following-sibling::text()[1]),19)` +- Modification date format: `02-Jan-2006 15:04` + +#### Apache httpd (Tested on 2.4.18) + +- Entry XPath: `//table/tbody/tr[position() > 2]` +- Filename XPath: `./td[2]/a` +- Modification time XPath: `./td[3]` +- File size XPath: `./td[4]` +- Modification date format: `2006-01-02 15:04` + +#### Caddy (Tested on v2.10.2) + +- Entry XPath: `//table/tbody/tr` +- Filename XPath: `./td[2]/a/span` +- Modification time XPath: `./td[4]/time` +- File size XPath: `./td[3]/div/div[2]` +- Modification date format: `01/02/2006 03:04:05 PM -07:00` + +#### Python SimpleHTTP (Tested on 3.11.5-0.6) + +- Entry XPath: `//ul/li` +- Filename XPath: `./a` +- Modification time XPath: leave blank +- File size XPath: leave blank +- Modification date format: leave blank + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/baidu.md b/pages/en/guide/drivers/baidu.md new file mode 100644 index 000000000..fbea8cf78 --- /dev/null +++ b/pages/en/guide/drivers/baidu.md @@ -0,0 +1,266 @@ +--- +top: 690 +categories: + - guide + - drivers +--- + +# Baidu Netdisk + +::: tip +Due to the limitation of Baidu Disk API, downloading files larger than about 20M needs to carry the header: "User-Agent": "pan.baidu.com", so when downloading files larger than 20M, you need to set the request header yourself, such as using curl: + +```bash +curl -L -X GET 'YOUR_LINK' -H 'User-Agent: pan.baidu.com' +``` + +Or use the proxy function in this program to transfer. +::: + +```mermaid +--- +title: How to Stream from Baidu Cloud? +--- +flowchart TB + a[(Baidu Cloud)] + 1[Official API] + b[Super VIP] + c[Modify UA] + d[Can Modify UA] + e[Cannot Modify UA] + f[Local Proxy] + g[High Bandwidth] + k[Low Bandwidth, Cannot Handle] + l[Can Stream] + m[End] + + a ==> 1 + 1 ==> b + b ==> c + b -.-> m + c ==> d + c -.-> e + d ==> l + e ==> f + f ==> g + f -.-> k + g ==> l + k ==> m + click c,e,d,m "#Add-User-Agent-Example" +``` + +## Refresh token + +**Currently, there are three methods to obtain a Baidu Netdisk refresh token:** + +All methods require you to [click here](https://api.oplist.org/) to obtain the refresh token. The **first method** requires developer permissions, while the **latter two** do not. + +1. If you have Baidu Netdisk developer permissions, please select **"Baidu Netdisk Auth Login"**, and in the Baidu Netdisk developer app backend, set the callback URL to the one provided on the webpage. After clicking "Get Token," you will automatically receive a refresh token. You can then configure your own `client_id` and `client_secret` in OpenList. + + (Please note: If using Baidu Netdisk's API, ensure the “Use Online API” option is unchecked, otherwise it will prevent connection.) + + ![](/img/drivers/baidu/dev_token.png) + + Want to obtain developer permissions? Please follow the steps below to apply for developer permissions on Baidu Cloud Open Platform: + + (Please note that we do not advocate the abuse of Baidu Netdisk developer permissions. You need to bear the operational risks yourself.) + 1. Please go to [Baidu Open Platform](https://pan.baidu.com/union). + + ![](/img/drivers/baidu/dev_platform.png) + + 2. Log in to your Baidu account. + + ![](/img/drivers/baidu/dev_platform_login.png){width=200px} + + 3. Follow the steps to complete the developer certification (supplement account information, such as real-name authentication, email, etc.). + + 4. After completion, the system will automatically redirect to the [Baidu Open Platform Console](https://pan.baidu.com/union/console/applist). + + If you cannot access the console, you may not have completed the certification. Please return to step one and try again. + + ![](/img/drivers/baidu/dev_platform_console.png){width=600px} + + 5. Create an application using the create button in the console. + + (Please note that individual developers can only create one application. If you have other uses for the application, please use methods two and three. Here we assume you don't have an application and are willing to create one for OpenList.) + + ![](/img/drivers/baidu/dev_app_create_button.png){width=600px} + + 6. Select "Software" as the application category, and fill in the application name and description according to your preference. Please note that these three contents cannot be changed once set, so please think carefully before filling them out. + + ![](/img/drivers/baidu/dev_app_create_panel.png){width=600px} + + 7. After creation is complete, you'll return to the control panel. Click on the application name to enter the application details page. + + (Please note that you don't need to apply for online review to use it normally.) + + ![](/img/drivers/baidu/dev_app_console.png){width=200px} + + 8. Select Security Settings and fill in the application callback URL with the provided callback address and save. + + ![](/img/drivers/baidu/dev_app_info.png){width=600px} + + ``` + https://api.oplist.org/baiduyun/callback + ``` + + ![](/img/drivers/baidu/dev_app_settings_safety.png){width=600px} + + 9. Return to the application details page and copy the necessary AppKey and SecretKey. + + 10. Use the [OpenList Token Acquisition Tool](https://api.oplist.org/) to log in via Baidu Netdisk authentication and fill in the corresponding keys to obtain the token. + + 11. Fill in the corresponding parameters and settings according to the image at the beginning of this method to start using it. + +2. This method uses a built-in API call via our intermediary server to refresh the access token. It is already available online. Simply check the option to use the parameters provided by OpenList, click "Get Token," and you will receive a refresh token. In the storage configuration page of OpenList, enable the "Use Online API" option, and enter the refresh token to start using it. + + ![](/img/drivers/baidu/non_dev_token.png) + + The basic principle of the online API is illustrated in the diagram below: + + ![](/img/drivers/baidu/openapi.png) + +3. If you do not have Baidu Netdisk developer permissions but somehow have access to an app’s `client_id` and `client_secret`, please select **"Baidu Netdisk OOB Authentication"**. By default, this method uses the client parameters from the "ES File Explorer" app, but you can also input your own `client_id` and `client_secret`. After clicking "Get Token," wait patiently as you are redirected to the Baidu authorization page. Log in and authorize the app, then copy the authorization code and return to the original webpage to input the code as instructed to obtain the refresh token. In the storage configuration page of OpenList, fill in the `client_id` and `client_secret` to start using the service. + + ![](/img/drivers/baidu/crack_dev_token.png) + +## Root folder path + +The root foler to mount, defaults to `/` + +- Mount a folder separately, according to the following format, `/` is the root directory, just extend to that directory if you want to mount that directory + - /folder_A/....../folder_x + +## ~~**Custom crack ua**~~ + +~~[**UA used when using 【Native Proxy & Crack API】**](https://github.com/alist-org/alist/issues/5602#issuecomment-1831188682)~~ The unofficial interface is no longer available + +## Download api + +- **Official:** The official interface, very stable, but for larger files, UA needs to be modified and the speed is slow (SVIP speed fast) +- **Crack:** unofficial interface,**Seems to be no longer available** ~~You must modify the UA and some files may not have a speed limit, but it will be unstable (not guaranteed 100%availability) Need to use a version greater than `3.19.0`~~ + - ~~Need to change the UA to`netdisk`,Modification method refer to [add-user-agent-user-example](#add-user-agent-usage-example)~~ + - ~~Or turn on the Web proxy, you do not need to modify the UA (you need a large broadband to use it stably)~~ + - ~~It is limited to play/download **`video (only tested in the mp4 format other formats are not tested)`**,If it is other format files, such a prompt will appear~~ + - ~~This is not a mistake, this is a limit, please do not panic.~~ +- **Crack video:** unofficial video interface, dedicated to playing videos, the following errors will occur in non-video formats, which is normal + - The browser also needs to modify the UA: `pan.baidu.com` or `netdisk` In short, if it is not the browser User-Agent, the video can be played. + - The specific situation is the same as the previous unofficial interface usage + - The continuous use time is unknown, and 100% availability is not guaranteed + + ```json{2-4,7-9} + { + error_code: 31119, + error_msg: "hit black userlist , hit illeage dlna", + request_id: 541111111111111140 + }, + { + error_code: 31329, + error_msg: "hit black userlist , hit illeage dlna", + request_id: 921111381111111100 + } + ``` + +## Add "User-Agent" usage example + +::: danger If you don't know how to set "User-Agent" see here +The following methods are limited to Baidu super member users + +Prompt again The following methods are limited to Baidu super member users + +It will only be useful if a member finishes modifying **`"User-Agent"`** (choose official and 302) + +If you don’t change **`"User-Agent"`**, you can enable=Web Agent, the disadvantage is that you need to build an OpenList machine for transfer, that is to say, you need a large broadband to help you transfer +::: + +
+

+ Tutorial on how to modify UA in web page 302 mode +

+
+
+
Official
+ +
+
+
Crack video (unavailable)
+ +
+
+
+ +### Web plugin + +The advantage of using a browser plug-in to modify is that it can be played directly online, of course, downloading is also possible. + +Example 1: If you really don’t know it, you can take a look at an example on the web page: **https://www.bilibili.com/video/BV1UA4y1X7J8** + +Example 2: Another plugin method covering 360, Chrome, Edge: **https://youtu.be/PP6b0WSzYMc** + +![bdUA](/img/drivers/baidu/bdUA.png) + +### Aria2 + +1. First follow the figure below to set the **`user-agent" `**, and then click the button option in the lower right corner, click ** gear ** (local settings) to configure the parameters well + + ![aria2-ua](/img/drivers/baidu/aria2-ua.png) + +2. Then open the third button option in the lower right corner (**Open the check box**), and then go to the list to select the file we want to download + +3. Select the files we need to download. After selecting, there will be a row of buttons. Select the second option on the right. One is sent to Aria2. + + If you use the webpage to modify the **`user-agent`**, you do not need to configure the `UA` of **Aria2**, and you can download it directly to Aria2 + +### Motrix + +1. First follow the figure below to set the **`user-agent" `**, and then click the button option in the lower right corner, click ** gear ** (local settings) to configure the parameters well + + ![motrix-ua](/img/drivers/baidu/motrix-ua.png) + +2. Then open the third button option in the lower right corner (**Open the check box**), and then go to the list to select the file we want to download + +3. Select the files we need to download. After selecting, there will be a row of buttons. Select the second option on the right. One is sent to Aria2. + + If you use the webpage to modify the **`user-agent`**, you do not need to configure the `UA` of **Motrix**, and you can download it directly to Aria2 + +- Motrix Download link:[Motrix Official website](https://motrix.app/),[Motrix-GitHub](https://github.com/agalwood/Motrix) + +## Upload Config + +Official Documentation: [百度网盘开放平台 - 上传 - 能力说明](https://pan.baidu.com/union/doc/3ksg0s9ye) + +> ~~Baidu Netdisk requires that each slice be uploaded within 30 seconds, so excessively high concurrency during file uploads may result in a significant number of failures.~~ +> Currently (November 2025), Baidu Netdisk appears to have significantly relaxed the timeout limit for uploading slices. +> Tests show that uploading single slice is now allowed to exceed 30 minutes. +> Therefore, after extending the timeout duration, the failure rate for batch uploading large files should be greatly reduced (even if low upload bandwidth). + +- Upload Threads: The number of slices to upload concurrently. +- Upload API: The domain endpoint used for uploading. +- Use dynamic upload API: Dynamically fetch upload domain, generally can achieve maximum upload speed. When enabled, the `Upload API` setting will be used as a fallback if failed to get online. This is enabled by default. +- Custom Upload Slice Size: Allows you to specify the size of each slice. Note that there are limitations, and this feature is available to VIPs only. +- Low Bandwidth Upload Mode: Attempts to address the frequent `Client.Timeout exceeded while awaiting headers` errors encountered in low upload bandwidth scenarios (e.g., residential broadband). When enabled, it uses the smallest possible slice size. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[local proxy]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[302]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/baidu_photo.md b/pages/en/guide/drivers/baidu_photo.md new file mode 100644 index 000000000..fc168e3bc --- /dev/null +++ b/pages/en/guide/drivers/baidu_photo.md @@ -0,0 +1,60 @@ +--- +top: 689 +categories: + - guide + - drivers +--- + +# Baidu Photo + + + +## Cookie + +Log in to [Baidu Photo](https://photo.baidu.com), open F12, and find any request containing the `Cookie` value. Copy it. + +![](/img/drivers/baidu/yike_cookie.png) + +## Album ID + +**When left blank, all albums in the root directory are displayed by default.** + +If you want to mount a single album, fill in the following: + +- The **Album ID** should be: {album_id}|{tid} + Example: `4021858707431029901|316519298447849660` + - **{album_id}**: After entering the album you want to mount, check the top URL for the ID after `/album`. This is the **{album_id}**. + - Example: [https://photo.baidu.com/photo/web/album/4021858707431029901](https://photo.baidu.com/photo/web/album/4021858707431029901) + - **4021858707431029901** is the **{album_id}** + + - **{tid}**: Access [this link](https://photo.baidu.com/youai/album/v1/list?limit=1000) to obtain the **{tid}** + - Once on the page, press `Ctrl+F` and search for the ID above. A few lines below you’ll find the corresponding **{tid}**. + +## Display Type + +Choose according to your needs. + +## Delete Source Files + +By default, it only removes the album, not permanently deletes the files. If you enable this option, the files will be permanently deleted after removal. Be cautious when enabling this. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/chaoxing.md b/pages/en/guide/drivers/chaoxing.md new file mode 100644 index 000000000..0c7d6a18f --- /dev/null +++ b/pages/en/guide/drivers/chaoxing.md @@ -0,0 +1,51 @@ +--- +top: 678 +categories: + - guide + - drivers +--- + +# ChaoxingxingGroupCloud + + + +chaoxingxing group cloud is also affiliated with chaoxingxing. After logging in to chaoxingxing, you can see a group under the left sidebar. + +Official website link:**https://i.chaoxing.com** + +### Username Password + +chaoxingxing account and password + +### Bbsid + +Follow the example process below to obtain + +![](/img/drivers/chaoxing/chaoxing_bbsid.png) + +### Root Folder ID + +The default is `-1`. For other subfolder ID acquisition methods, see the example below. + +- Search the keyword `getResourceList` in the request. For convenience, we can directly search `list` and see it. Then we enter the folder we need to mount. After entering the folder, we can find the `folder id by looking at the request.` + + ![](/img/drivers/chaoxing/chaoxing_folder_id.gif) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/chunk.md b/pages/en/guide/drivers/chunk.md new file mode 100644 index 000000000..19bfcfbb2 --- /dev/null +++ b/pages/en/guide/drivers/chunk.md @@ -0,0 +1,113 @@ +--- +top: 995 +categories: + - guide + - drivers +--- + +# Chunk + +The Chunk is used to split large files into multiple file chunks. Each chunk is stored as an independent file on the backend driver. + +The size of each file chunk can be manually configured by the user. Except for the last chunk of each file—which is smaller than the configured size—all other chunks are always equal to the specified size. + +A chunked file is stored as a folder on the backend driver, containing all its chunks. + +![chunk_example](/img/drivers/chunk/example.png) + +## Setup Instructions + +- **Remote path:** The real storage path of the chunked files. This path must be the root path or a subpath of another driver. +- **Part size:** Maximum chunk size / Size of chunks except the last, in bytes. +- **Chunk large file only:** Whether to only chunk files larger than Part size. + - When disabled: + ![chunk_large_only_disabled](/img/drivers/chunk/chunk_large_only_disabled.png) + - When enabled: + ![chunk_large_only_enabled](/img/drivers/chunk/chunk_large_only_enabled.png) +- **Chunk prefix:** The prefix for the names of chunk folders stored on the backend driver, representing a chunked file. This is used to identify whether a folder is a regular folder or a chunked file storing folder, and therefore cannot be empty. +- **Custom ext:** Custom suffix for chunk names, used to bypass limitations of certain drivers. + - When left empty:![custom_ext_disabled](/img/drivers/chunk/custom_ext_disabled.png) + - When setting to `.jpg`:![custom_ext_enabled](/img/drivers/chunk/custom_ext_enabled.png) + +::: warning TIP +Once the driver is created, Custom ext should not be modified again, otherwise previously uploaded chunked files will become unrecognizable. + +If modification is necessary, you must manually change the suffix of all chunks on the backend driver afterward. +::: + +- **Store hash:** Whether to store the hashes of chunked files in the chunk folder as well. + This feature does not actively compute hashes; it only stores the hashes that the file already carries when uploading. +- **Num list workers:** When handling `List` requests, if a folder contains chunk folders, it is necessary to further list those chunk folders to retrieve information such as file size and hash value. Using multiple threads can speed up this process. A higher number of threads consumes more CPU and bandwidth resources but also increases speed. It is recommended to be disabled (set to 1) when the backend driver has API rate limiting. + +## Use in combination with Crypt + +If you need to both chunk and encrypt files, it is recommended to **encrypt first, then chunk**. Specifically, set Remote path of the [Crypt](/en/guide/drivers/crypt) driver to the mount path of the **Chunk** driver, and set Remote path of the **Chunk** driver to the actual storage path of the files. + +- Best practice: + +```mermaid +graph LR + File[Upload stream] --> Crypt + Crypt --> Chunk + Chunk -->|chunk1| Backend[Backend driver] + Chunk -->|chunk2| Backend + Chunk -->|chunk3| Backend +``` + +- Bad practice: + +```mermaid +graph LR + File[Upload stream] --> Chunk + Chunk -->|chunk1| Crypt1[Encryption algorithm] + Chunk -->|chunk2| Crypt2[Encryption algorithm] + Chunk -->|chunk3| Crypt3[Encryption algorithm] + Crypt1 -->|encrypted chunk 1| Backend[Backend driver] + Crypt2 -->|encrypted chunk 2| Backend + Crypt3 -->|encrypted chunk 3| Backend + subgraph Crypt + Crypt1 + Crypt2 + Crypt3 + end +``` + +**Reason**: The bad practice involves storing a series of encryption metadata in each file chunk, which consumes more space. It also fails to guarantee that the chunks adhere to the user-specified maximum size, thereby undermining the purpose of file chunking. + +To perform emergency recovery on an encrypted file chunked according to the best practice, you simply need to concatenate all the chunks in order and then proceed with recovery. + +## Use in combination with Alias + +If you need to store chunked files across multiple load-balanced drivers, you can combine this with the [Alias](/en/guide/drivers/alias) driver. For details, refer to [Load Balancing / Load balancing by file chunks](/en/guide/advanced/balance#load-balancing-by-file-chunks). + +It's important to note that, unlike the approach used with Crypt, when combining with Alias, the process should be **chunk first, then load balance**. This means setting the Remote path of the **Chunk** driver to the mount path of the **Alias** driver, and setting the paths within the **Alias** driver to the actual file storage paths. + +- Best Practice: + +```mermaid +graph LR + File[Upload Stream] -->|File1| Chunk + File -->|File2| Chunk + Chunk -->|File1 Block1, File1 Block2, File1 Block3| Alias + Chunk -->|File2 Block1, File2 Block2| Alias + Alias -->|File1 Block3| D1[Backend Driver 1] + Alias -->|File1 Block2| D2[Backend Driver 2] + Alias -->|File1 Block1| D3[Backend Driver 3] + Alias -->|File2 Block1| D1 + Alias -->|File2 Block2| D3 +``` + +- Bad Practice: + +```mermaid +graph LR + File[Upload Stream] -->|File1| Alias + File -->|File2| Alias + Alias -->|File1| Chunk1[Chunk 1] + Alias -->|File2| Chunk2[Chunk 2] + Chunk1 -->|File1 Block1| D1[Backend Driver 1] + Chunk1 -->|File1 Block2| D1 + Chunk1 -->|File1 Block3| D1 + Chunk2 -->|File2 Block1| D2[Backend Driver 2] + Chunk2 -->|File2 Block2| D2 +``` diff --git a/pages/en/guide/drivers/cloudflare-imgbed.md b/pages/en/guide/drivers/cloudflare-imgbed.md new file mode 100644 index 000000000..f5a8d92a1 --- /dev/null +++ b/pages/en/guide/drivers/cloudflare-imgbed.md @@ -0,0 +1,83 @@ +--- +top: 700 +categories: + - guide + - drivers +--- + +# CloudFlare-ImgBed + + + +## Disclaimer + +::: warning +This driver only provides an interface to interact with the CloudFlare-ImgBed backend and is not responsible for the availability and stability of the image hosting program itself or its storage channels. +Users are solely responsible for determining and configuring the storage channels used (e.g., Cloudflare R2, HuggingFace, S3, Telegram) and must strictly comply with the Terms of Service and acceptable use policies of the relevant platforms and service providers. Do not use this driver for any illegal activities, rights infringement, or abuse of platform resources (e.g., excessive consumption of free quotas, illegal storage/distribution of copyrighted content). +All consequences arising from improper configuration, violation of platform policies, or service abuse (including but not limited to account suspension, data loss, service interruption, etc.) shall be borne by the user. OpenList and its developers shall not be held liable. +::: + +## 1. Preparation + +Before configuring this driver, you need to deploy your own CloudFlare-ImgBed backend. +Project address: [MarSeventh/CloudFlare-ImgBed](https://github.com/MarSeventh/CloudFlare-ImgBed) +After deployment, log in to the backend dashboard (`https://your-domain/dashboard`), and ensure that at least one storage channel (e.g., HuggingFace, Cloudflare R2, S3, Telegram) is configured in the system settings. + +## 2. Add in OpenList + +### Mount Path + +Enter the path you want to mount to, for example `/imgbed`. + +### Root Folder Path + +Default is `/`, can be left empty. + +### Address + +Enter the address of your deployed image hosting service, e.g., `https://img.example.com`. No need to add `/` at the end. + +### Token + +Enter the API Token generated in the backend system settings. +![](/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png) + +### Small Channel Name + +Typically used for uploading files smaller than 20MB. Enter the channel name configured in your backend (e.g., `my cfr2`, `my telegram`). + +### Large Channel Name + +Used for uploading files larger than 20MB. It is recommended to configure a channel that supports large files (e.g., `huggingface`). If left blank, large files will fall back to the small channel for upload. + +### Large Channel Type + +Select the corresponding type based on your large file channel: + +- `huggingface`: Uses HuggingFace LFS direct upload (supports instant upload and chunking) +- `telegram` / `cfr2` / `s3` / `discord`: Uses the backend chunked upload API + +### Upload Thread + +Concurrent thread count for HuggingFace chunked direct upload, default is `3`, max is `32`. Can be increased if network conditions are good. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/cloudreve.md b/pages/en/guide/drivers/cloudreve.md new file mode 100644 index 000000000..e2efc2db2 --- /dev/null +++ b/pages/en/guide/drivers/cloudreve.md @@ -0,0 +1,72 @@ +--- +top: 498 +categories: + - guide + - drivers +--- + +# Cloudreve V3 + +## Parameters + +### Address + +The address of the Cloudreve V3 server, such as: `https://www.example.com`. It is better to remove the slash `/` after it. + +### Authentication + +Cloudreve V3 supports authentication using Cookie. The mounting authentication methods are divided into: + +1. `Username` + `Password`: Automatically uses the login interface to obtain Cookie, there might be issues with CAPTCHA. +2. `Cookie` Only: Can be temporarily used but will expire and cannot be renewed. Parameters can be found from browser requests or Local Storage. +3. [Reference](../drivers/common.html#Reference): Fill in `ref:/{mount path}` in `Remark`: Reference authentication, tokens, etc., from "Mounted Storage". + +::: tip Steps to get cookie: +Only the string following cloudreve-session= is required. +::: + +![cloudreve_get_cookie](/img/drivers/cloudreve/cloudreve_get_cookie.png) + +### Root Folder Path + +Default is `/`, which can be obtained from the `?path=` in the web link. The obtained parameters may need to be [URL decoded](https://www.google.com/search?q=URL+decode). + +### Enable Folder Size + +Enable Cloudreve V3 server to calculate the size of each folder and generate thumbnails for each file. Enabling this feature may cause server errors or slow performance, and it is `disabled` by default. + +### Custom UA + +Used to customize the `User-Agent` header information used in requests. Leaving it blank will use OpenList default settings. + +## Upload + +The supported storage policies for uploading are as follows: + +- Local storage +- Slave storage +- OneDrive +- S3 + +Upload storage policies can be set within Cloudreve V4 web interface by entering the corresponding folder (requires the server to be Cloudreve Pro). + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/cloudreve_v4.md b/pages/en/guide/drivers/cloudreve_v4.md new file mode 100644 index 000000000..6cac42195 --- /dev/null +++ b/pages/en/guide/drivers/cloudreve_v4.md @@ -0,0 +1,116 @@ +--- +top: 497 +categories: + - guide + - drivers +--- + +# Cloudreve V4 / Share + +## Parameter Explanation + +### Address + +The address of the Cloudreve V4 server, such as: `https://www.example.com`. It is better to remove the slash `/` after it. + +### Authentication + +Cloudreve V4 supports authentication using Token. The mounting authentication methods are divided into: + +1. `Username` + `Password`: Automatically uses the login interface to obtain Access Token and Refresh Token, there might be issues with CAPTCHA. +2. `Refresh Token` Only: Automatically uses the refresh interface to renew Access Token and Refresh Token, parameters can be found from browser requests or Local Storage. +3. `Access Token` Only: Can be temporarily used but will expire and cannot be renewed. +4. None: Anonymous user, suitable for public sharing. +5. [Reference](../drivers/common.html#reference): Fill in `ref:/{mount path}` in `Remark`: Reference authentication, tokens, etc., from "Mounted Storage". + +### Steps to get tokens + +#### From Local Storage + +::: tip +There may be multiple sessions; please select the one you want to mount. +::: + +![cloudreve_v4_token_local_storage](/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png) + +#### From Network + +::: tip +You may need to log in anew to see this request. +::: + +![cloudreve_v4_token_resp](/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png) + +### Root Folder Path + +Cloudreve V4 uses a custom URI as the path, which can be obtained from the `?path=` in the web link. The obtained parameters may need to be [URL decoded](https://www.google.com/search?q=URL+decode). + +#### Mounting My Files + +Default is `cloudreve://my/`, listing user files. + +#### Mounting Share + +Supports mounting folder-type shares, the path should be filled as: `cloudreve://{ShareID}@share/`. + +- ShareID is the parameter after `/s/` in the share link. +- Currently, Cloudreve V4 does not support creating password-protected shares. However, if the data is migrated from V3, the previously created sharing password will be retained. The URI of the share link with the password (`/s/{shareID}/{sharePassword}`) is `cloudreve://{shareID}:{sharePassword}@share`. + +### Enable Folder Size + +Enable Cloudreve V4 server to calculate the size of each folder. Enabling this feature may cause server errors or slow performance, and it is `disabled` by default. + +### Enable Thumbnails + +Enable Cloudreve V4 server to generate thumbnails for each file. Enabling this feature may cause server errors or slow performance, and it is `disabled` by default. + +### Enable Version Upload + +Enable overwrite upload and keep the previous version, which consumes extra space. Disabled by default, delete the existing file before overwriting upload. + +### Custom UA + +Used to customize the `User-Agent` header information used in requests. Leaving it blank will use OpenList default settings. + +### Sorting + +Supports changing the sorting parameters when requesting lists. + +### Hide files currently being uploaded + +Filter files with upload sessions (in progress) based on metadata information, size 0, unable to download + +## Upload + +The supported storage policies for uploading are as follows: + +- Local storage +- Slave storage +- OneDrive +- S3 +- KS3 + +Upload storage policies can be set within Cloudreve V4 web interface by entering the corresponding folder (requires the server to be Cloudreve Pro). + +If upload permissions are enabled for shares, uploads are supported. It requires the server to check "Enhance anonymous user permissions" for the user group (requires the server to be Cloudreve Pro). + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/cnb_releases.md b/pages/en/guide/drivers/cnb_releases.md new file mode 100644 index 000000000..78e12ad61 --- /dev/null +++ b/pages/en/guide/drivers/cnb_releases.md @@ -0,0 +1,61 @@ +--- +categories: + - guide + - drivers +top: 397 +--- + +# CNB Releases + +https://cnb.cool/ + +## Known issues + +CNB releases are not a standard file system and have some unresolved issues. Please do not submit feedback for these. + +1. An empty repository needs to be initialized first. +2. Subdirectories are not supported under Tag / Release directories. +3. The OpenAPI upload returns `expires_in_sec` as only 10 seconds. Uploading large files may time out; after time out, the upload continues but returns `invalid token`. Therefore, a local timeout mechanism is added to automatically stop uploading when the set time is reached, avoiding failed uploads and wasted bandwidth. +4. `Move`, `Copy`, and `Rename` operations are not supported. +5. Only when `UseTagName` is disabled, renaming can be done by modifying the Release name. + +## Parameters + +### Repo + +Only one repository can be specified. To reuse a token, please use the [Reference](common.md#reference) feature. + +### Token + +Access token. No need to include `Bearer`. Supports the [Reference](common.md#reference) feature. + +How to obtain: After logging in, go to `Personal Settings` - [Access Token](https://cnb.cool/profile/token) -> `Add Access Token`. + +For listing, grant the `repo-code:r` permission. For modifications, grant the `repo-code:rw` permission. + +### UseTagName + +Use the original Tag name instead of the Release name. + +By default, this is disabled and the Release name is used, which supports renaming. Tag names cannot be changed once created. When creating a new folder for the first time, a Tag with the folder name will be published. Renaming an existing Release will modify the Release name. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/common.md b/pages/en/guide/drivers/common.md new file mode 100644 index 000000000..ce63d7b79 --- /dev/null +++ b/pages/en/guide/drivers/common.md @@ -0,0 +1,207 @@ +--- +top: 1000 +categories: + - guide + - drivers +--- + +# Common + +## Mount Path + +The unique identifier for the mount point, the name displayed externally, and the location where it should be mounted. If you want to mount it to the root directory, please enter `/`. + +![](/img/drivers/common/path_b.png#light) + +![](/img/drivers/common/path_h.png#dark) + +::: danger +You cannot use duplicate mount path names, otherwise, the following error will occur: + +```json +Failed to create storage in database: UNIQUE constraint failed: x_storages.mount_path +``` + +Solution: Use [aliases](./alias.md) to aggregate multiple mount points. +::: + +::: danger +The mount path name is a required field and cannot be left empty, or the following error will occur: + +```json +Key: 'Storage.MountPath' Error: Field validation for 'MountPath' failed on the 'required' tag +``` + +Solution: If you want to mount to the root directory, please enter `/`. +::: + +## Order + +When mounting multiple drives, this is used for sorting. The smaller the number, the further to the front. Negative numbers can also be used. + +## Remark + +You can add notes for easier management. + +### Reference + +Reference authentication, tokens, etc., from the **"Mounted Storage"** to enable sharing the same token between multiple cloud drives. + +Currently, the following cloud drives are supported: + +- 139Yun +- AliyundriveOpen +- 189CloudPC +- 123PanShare(ref 123Pan) +- Cloudreve V3 / V4 + +**How to use**: In the storage settings, set the first line of **Remark** to: **ref:/mount_path** + +**Important**: `ref:/` should be in lowercase letters and symbols. + +![](/img/drivers/ref_token.png) + +## Enable signing + +Sign and encrypt files (no password required), only valid for this driver, if other signatures are not enabled and `signature all` and `meta-information encryption` are not set, others will not be signed. + +Usage scenario: I don't want to enable all signatures, and I don't want to set metadata encryption. I just want to sign and encrypt a certain driver to prevent it from being scanned. + +Scope of influence: `Settings-->Global-->Signature All` > `Metainformation Directory Encryption` > `Single Driver Signature`. + +## Disable index + +Allow users to disable storage indexing. + +- For example, if you enable `Ignore Index` in the index options, you no longer need to configure it after enabling `Disable Index`, which is more convenient. + +## Cache Expiration + +Cache time of directory structure. + +## Custom Cache Policies + +Cache time for directory paths (in minutes). + +You can customize the cache time for specific file paths using pattern matching. The configuration supports wildcard patterns: + +- `*` matches a **single** directory level. +- `**` matches **multiple** directory levels. + +Example configuration: + +```txt +/Series/Completed/*:60 +/Series/Updating/*/**:10 +/Series/Archived/**:30 +``` + +Explanation: + +- `*` matches only a single directory level. Items directly under `/Series/Completed` will be cached for 60 minutes. This does **not** include deeper subdirectories — for example, `/Series/Completed/A/B` will not match this rule. +- `**` matches multiple directory levels. Therefore, the contents of subdirectories under `/Series/Updating` will be cached for 10 minutes. For example, `/Series/Updating/A/B` and `/Series/Updating/C/D` will match. +- The pattern `/Series/Updating/*/**` enforces a “single level followed by multi-level” match. As a result, directories directly under `/Series/Updating` will **not** be matched by this rule. +- All contents under `/Series/Archived` (including any depth of subdirectories) will be cached for 30 minutes. + +## Web proxy + +Whether the web preview,download and the direct link go through the transfer. If you open this, recommended you set [site_url](../../configuration/configuration.md#site-url) so that OpenList can works fine. + +::: tip + +- **Web proxy Strategies:** It is a strategy when using the webpage. The default is a local agent. If you fill in the proxy URL and enable the web agent to use the proxy URL +- **Webdav policy Strategies:** It is an option to use the webdav function + - If there are 302 options default to 302, if there is no 302 option default to the local agent, if you want to use the agent URL, please fill in and manually switch to the proxy URL strategy + +The two are different configurations. + +::: + +## Webdav policy + +- **302 redirect:** redirect to the real link + - Although it does not consume traffic, it is not recommended to share and use it. +- **use proxy URL:** redirect to proxy URL + - It will consume the traffic of the agent URL +- **native proxy:** return data directly through local transit(best compatibility) + - The traffic of the construction of OpenList device will consume + +### Description of three modes + +```mermaid +--- +title: 302 redirect, use proxy URL, native proxy +--- +flowchart LR + E(User operation OpenList calls API
triggers related API operations Cloud Drive
Cloud Drive receives command and begins execution) + A[(Cloud Drive)] + B(You) + C[Use proxy URL] + D{Native proxy} + E==>A==>E + A==>|Directly transferring from cloud drive to you without any loss
302 downloads as fast as possible|B + A-->|Send the cloud disk to the use proxy URL first|C-->|After being forwarded to you by the proxy URL, your download speed depends on the speed of downloading, forwarding, and forwarding on the other end of the proxy URL|B + A-->|Send the cloud drive to the device running OpenList first|D-->|After being forwarded to you by the device running OpenList, your download speed depends on the speed of downloading, uploading, and forwarding on the end where OpenList is built|B +``` + +## Download proxy URL + +When the proxy is turned on without filling in this field, the local machine will be used for transfer by default. + +### 1. Cloudflare Workers + +Here’s the translation: + +You can use Cloudflare Workers as a proxy. Simply fill in your Cloudflare Workers address here. + +The code to set up Workers can be found at [https://github.com/OpenListTeam/OpenList-Proxy/blob/main/openlist-proxy.js](https://github.com/OpenListTeam/OpenList-Proxy/blob/main/openlist-proxy.js). When using it, you need to replace the following variables: + +- `ADDRESS`: Your OpenList address, which must include the protocol header and should not end with a `/`. For example, `https://pan.example.com`. + +- `TOKEN`: The [Token](../../configuration/other.md#token) of the admin account, which can be found in the “Other Settings” section of the OpenList admin page. + +- `WORKER_ADDRESS`: Your Worker address, which is usually the same as the **Download Proxy URL**. + + :warning: Cloudflare Workers free CDN support is only compatible with **http80** and **https443** ports (whether domestic or international), as tested by group members. + +When filling in the **Download Proxy URL** in the OpenList backend configuration, the link should not end with a `/`. + +Detailed text tutorial: + +### 2. Universal Binary + +You can use another machine as a proxy. Download the program from https://github.com/OpenListTeam/OpenList-Proxy/releases and check the usage instructions with `./openlist-proxy -help`. + +Detailed text tutorial: + +### 3. Developing on your own + +You can develop your own proxy program. The general steps are as follows: + +- When downloading, it will request `PROXY_URL/path?sign=sign_value`. +- In the proxy program, validate the `sign`. The calculation method for `sign` is: + +```js +const to_sign = `${path}:${expireTimeStamp}` +const _sign = safeBase64(hmac_sha256(to_sign, TOKEN)) +const sign = `${_sign}:${expireTimeStamp}` +``` + +`TOKEN` is the [Token](../../configuration/other.md#token) of the administrator account, which can be obtained in the “Other Settings” section of the OpenList management page. + +- After validating the signature, request `HOST/api/fs/link` to obtain the file URL and the request headers to include. +- Use the information to make the request and handle the response. + +## Sort related + +- **Sort by**: Sort by what +- **Sort direction**: Whether the sort direction is ascending or descending + +::: info +Some drives use their own sorting method, which may be different. +::: + +## Extract folder + +- **Extract to front**: put all folders to the front when sorting +- **Extract to back**: put all folders to the back when sorting diff --git a/pages/en/guide/drivers/crypt.md b/pages/en/guide/drivers/crypt.md new file mode 100644 index 000000000..d4df34c89 --- /dev/null +++ b/pages/en/guide/drivers/crypt.md @@ -0,0 +1,222 @@ +--- +top: 996 +categories: + - guide + - drivers +--- + +# Crypt + +The Crypt driver provides secure encryption for your files and folders, acting as a two-password-protected vault. Only users with the correct password and salt can access the encrypted content. + +**Key Features:** + +- File and folder encryption with multiple security levels +- Password and salt-based protection +- Compatible with rclone crypt +- Supports various encryption modes for filenames and directories + +::: warning Important Security Notice + +1. Read this guide thoroughly before using the Crypt driver +2. Test the configuration in a local environment before production deployment +3. **Never modify the configuration after storing encrypted data** - this will make data lost +4. Keep your password and salt values secure - losing them means losing access to your data + +Again, please read the documentation carefully; otherwise, any data loss will be at your own risk! +::: + +## Setup Instructions + +1. **Create Storage Location** + + Create an **empty folder** in your existing mounted drive to store encrypted files. + +2. **Configure Remote Path** + + Enter the path of the empty folder in the `Remote path` field of your Crypt driver configuration. + + **Example Configuration:** + - Original driver path: `/123` + - New empty folder: `/123/encrypted_storage` + - Remote path setting: `/123/encrypted_storage` + +3. **Upload Files** + + Upload files to the newly created Crypt driver mount point. Only files uploaded through the Crypt driver will be encrypted. + + **File Access:** + - **Encrypted files**: Located in the remote path, appear scrambled and cannot be opened directly + - **Decrypted access**: View and access files normally through the Crypt driver mount point + +### Basic Configuration Example + +For users new to encryption, use the following default configuration: + +![Crypt Configuration Example](/img/drivers/crypt/crypt-demo1.png) + +::: danger Please make sure to read the following important notes carefully to ensure understanding! + +**Important Declaration:** + +- **Do not modify the configuration! Do not modify the configuration! Do not modify the configuration!** Once the configuration is saved, do not modify it again!!! This is repeated for emphasis! +- **Password** and **Salt** must be remembered! After clicking save, they will be encrypted and cannot be displayed in plain text (the plain text in the image shows the state before saving). + +> [**Password Configuration Instructions**](#password) +> +> [**Salt Configuration Instructions**](#salt) + +--- + +- **If you have not yet uploaded files within the Crypt driver**, you can modify the password and configuration. **Otherwise, do not modify!** +- After modifying the configuration, Crypt will attempt to filter illegal files/directories, but illegal data will not be automatically deleted. + - **Illegal files/directories** refer to encrypted data generated with a different configuration. + +::: + +::: warning +Regarding encryption combinations, there are 5 options available (actually 6), but it’s important to note that if only folder encryption is enabled without encrypting the file names, the configuration will not take effect (as shown in the first example below). + +| Filename Encryption | Directory Encryption | Status | +| ------------------- | -------------------- | ---------- | +| `Off` | `Enabled` | ❌ Invalid | +| `Off` | `Disabled` | ✅ Valid | +| `Standard` | `Disabled` | ✅ Valid | +| `Standard` | `Enabled` | ✅ Valid | +| `Obfuscate` | `Disabled` | ✅ Valid | +| `Obfuscate` | `Enabled` | ✅ Valid | + +::: + +## Configuration Options + +### Filename Encryption + +**Default:** `Disabled` + +**Available Options:** + +| Mode | Security Level | Description | +| ----------- | -------------- | ----------------------------------------------------------------------------- | +| `Off` | None | Files keep original names with encrypted suffix (e.g., `file.txt.bin`) | +| `Standard` | High | **Recommended** - Strong encryption with good security | +| `Obfuscate` | Low | Simple obfuscation, supports long filenames but may create special characters | + +--- + +In the image below, the left side shows file **name encryption** and folder **name encryption** enabled, while the right side shows the decrypted Crypt driver where files can be viewed. + +- **File name encryption not enabled**: Original name + encrypted suffix, as shown in the top left corner +- **File name encryption enabled**: Fully encrypted file name, content cannot be recognized, as shown in the bottom left corner + +![Filename Encryption Comparison](/img/drivers/crypt/crypt-demo2.png) + +### Directory Name Encryption + +**Default:** `Disabled` + +Directory encryption requires filename encryption to be enabled. When activated, folder names are also encrypted for enhanced security. + +**Configuration Combinations:** + +| Filename Encryption | Directory Encryption | Status | +| ------------------- | -------------------- | ---------- | +| `Off` | `Enabled` | ❌ Invalid | +| `Off` | `Disabled` | ✅ Valid | +| `Standard` | `Disabled` | ✅ Valid | +| `Standard` | `Enabled` | ✅ Valid | +| `Obfuscate` | `Disabled` | ✅ Valid | +| `Obfuscate` | `Enabled` | ✅ Valid | + +### Remote Path + +The storage location for encrypted files. Can be any mountable drive supported by OpenList. + +### Security Parameters + +::: danger +After saving the configuration, both password and salt values are encrypted and cannot be displayed in plain text. Store them securely in a separate location. +::: + +#### Password + +::: warning +Note that passwords exported using `rclone config` are obfuscated and cannot be used directly. Please prefix the password with `___Obfuscated___` before use.
+For example, if the exported password is `abc123xyz`, it should be entered as `___Obfuscated___abc123xyz` in the Crypt driver. +::: + +Primary encryption key. **Must be remembered** - cannot be recovered if lost. + +#### Salt + +::: warning +Note that salts exported using `rclone config` are obfuscated and cannot be used directly. Please prefix the salt with `___Obfuscated___` before use.
+For example, if the exported salt is `abc123xyz`, it should be entered as `___Obfuscated___abc123xyz` in the Crypt driver. +::: + +Secondary encryption key, acts as an additional password layer. **Must be remembered** - cannot be recovered if lost. + +If you don't know what is salt, treat it as a second password. Optional but recommended + +### Advanced Options + +#### Encrypted Suffix + +**Default:** `.bin` + +Custom suffix for encrypted files (only used when filename encryption is disabled). Must start with a dot (e.g., `.abc`, `.encrypted`). + +#### Filename Encoding + +**Default:** `base64` + +**Warning:** Only modify if you understand the implications. Other encoding options are not thoroughly tested and may cause compatibility issues. + +For rclone compatibility, configure this setting in advanced options. + +## Advanced Usage + +### Rclone Compatibility + +The Crypt driver is fully compatible with [rclone crypt](https://rclone.org/crypt). + +**Important Notes:** + +- OpenList Crypt uses `filename_encoding = base64` by default for better long filename support, configure this setting in advanced options when using with rclone +- Case-insensitive filesystems (e.g., Windows with local storage) may cause issues + +## Troubleshooting + +### Startup Errors + +If Crypt shows errors during OpenList startup, it's likely because Crypt starts before its target path is available. + +**Solution:** Set a higher [order number](./common.md#order) for the Crypt driver to delay its initialization. + +### Data Access Issues + +If you cannot access previously encrypted data: + +1. Verify that the password and salt values are correct +2. Ensure the configuration hasn't been modified +3. Check that the remote path is accessible +4. Confirm the filename encoding setting matches your original configuration + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/degoo.md b/pages/en/guide/drivers/degoo.md new file mode 100644 index 000000000..76b707b06 --- /dev/null +++ b/pages/en/guide/drivers/degoo.md @@ -0,0 +1,55 @@ +--- +categories: + - guide + - drivers +top: 587 +--- + +# Degoo + +https://degoo.com/ + +**Authentication methods**: + +1. Username + Password +2. Refresh Token +3. Access Token + +In normal cases, you can log in with your username + password. If you encounter a 429 error, you can log in using tokens. You can obtain the token from the request body or header in your browser. + +## Username + +Your user's name. + +## Password + +Your user's password. + +## Refresh Token + +Refresh token for automatic token renewal, obtained automatically. + +## Access Token + +Access token for Degoo API, obtained automatically. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/develop.md b/pages/en/guide/drivers/develop.md new file mode 100644 index 000000000..6ff2c1a8e --- /dev/null +++ b/pages/en/guide/drivers/develop.md @@ -0,0 +1,11 @@ +--- +top: 100 +categories: + - guide + - drivers +--- + +# Contribute new storage driver + +Duplicate `drivers/template` to expand the new storage. Please read [CONTRIBUTING](https://github.com/OpenListTeam/OpenList/blob/main/CONTRIBUTING.md) for details. +The design of the driver part of OpenList is easy to expand, no need to modify other files, just create a new package under the back-end project drivers package and reference this package in the `all.go` file. diff --git a/pages/en/guide/drivers/doubao.md b/pages/en/guide/drivers/doubao.md new file mode 100644 index 000000000..d9b34312b --- /dev/null +++ b/pages/en/guide/drivers/doubao.md @@ -0,0 +1,58 @@ +--- +top: 676 +categories: + - guide + - drivers +--- + +# Doubao Drive + + + +Mount [Doubao Drive](https://www.doubao.com/chat/drive/) cloud storage. + +::: danger Security Risk + +Do not use 302 if the storage is public accessible. +Otherwise, the download link may leak sensitive information such as access token or signature. +Others may use the leaked link to **access all your files**. + +::: + +## Root folder ID + +The root folder ID is the parameter value displayed in the address bar when accessing Doubao Drive via a web page. For example, after accessing [Doubao Drive](https://www.doubao.com/chat/drive/), the address bar shows `https://www.doubao.com/chat/drive/AAAAAAAAAAAAAAAAAAAAAAAAAA?tab=myUpload`, where `AAAAAAAAAAAAAAAAAAAAAAAAAA` is the root folder ID. + +![Doubao Drive Root Folder ID](/img/drivers/doubao/doubao-root-folder-id.png) + +## Cookie + +Required. Web cookie. Used to refresh token and extract Authorization/DPoP tokens. + +Open browser developer tools, visit [Doubao Drive](https://www.doubao.com/drive/), log in to your account, check the `Network` tab, search for `biz_auth` and open it, find `Cookie` in the `Header`, copy the complete value and fill it in. + +![Doubao Drive Cookie](/img/drivers/doubao/doubao-cookie.png) + +OpenList will automatically extract other attributes from the Cookie. Please ensure that the Cookie contains the following key-value pairs: + +- `LARK_SUITE_DPOP` +- `LARK_SUITE_ACCESS_TOKEN` +- `feishu_dpop_keypair` (used for key generation, optional) + +## App ID + +必填,Doubao's App ID. The App ID may vary for different clients, please refer to the actual value. + +## DPoP Key Secret, Auth Client ID, Auth Scope, Auth SDK Source, Auth SDK Version + +Optional, for Token refresh. Please obtain it yourself, and refer to the actual value. + +## Share Link + +Whether to use share link for download. If enabled, OpenList will create share and request with anonymous user's token to create download link. This can reduce authorization leak. + +Please ensure you have filled all authentication options above before enabling this function. + +## Ignore JWT Check + +Whether to ignore JWT check to prevent time issue diff --git a/pages/en/guide/drivers/dropbox.md b/pages/en/guide/drivers/dropbox.md new file mode 100644 index 000000000..481156524 --- /dev/null +++ b/pages/en/guide/drivers/dropbox.md @@ -0,0 +1,119 @@ +--- +top: 590 +categories: + - guide + - drivers +--- + +# Dropbox + +Dropbox Official:https://www.dropbox.com/ + +## Get refresh token + +- **This drive does not support the online API mode provided by OpenList.** + +- The following tutorial applies when the option is kept disabled as shown below. + + ![Disable online_api](/img/drivers/dropbox/7.png) + +- If you create your own `Client ID` and `Secret`, remember to authorize them. + +The steps are as follows: + +1. First, [click here](https://www.dropbox.com/developers/apps?_tk=pilot_lp&_ad=topbar4&_camp=myapps) to enter the Dropbox app management page and click "Create App". + + ![Enter the page](/img/drivers/dropbox/1.png) + +2. After entering the app, configure the app type as shown below. + + ![App type](/img/drivers/dropbox/2.png) + +3. You can get the id and secret in the red box: the upper one is the id, the lower one is the secret. + + ![Parameter location](/img/drivers/dropbox/6.png) + +4. Configure the callback URL. If you are strict about permissions and do not want to use an external callback address, you can set a local address here, or use the one outside the red box. + + ![Callback URL](/img/drivers/dropbox/3.png) + +5. Finally, go to the permissions configuration page to set the app's permissions. + + ![Permission configuration](/img/drivers/dropbox/4.png) + +6. [Click here](https://api.oplist.org/) to enter the token acquisition tool. Select Dropbox, fill in your id and secret, and after authorization you can get the refresh token. + +7. In the OpenList configuration page, enter the refresh token, id, and secret to use. Note that the refresh token is about 40-50 characters long. + +![OpenList configuration](/img/drivers/dropbox/5.png) + +If you are highly privacy-conscious, Dropbox supports local callback. You can use the following script provided by GPT to quickly implement it, communicating only with Dropbox servers. + +::: warning +Since the callback address is local and you have not set up a real local callback server, please manually copy the authorization code from the browser address bar. + +**Please resolve Python environment issues yourself, or use the callback server provided above.** +::: + +```python +import requests +import webbrowser +# Please replace with your own Dropbox App information +CLIENT_ID = 'your_app_key' +CLIENT_SECRET = 'your_app_secret' +REDIRECT_URI = 'http://localhost:114514' +# Step 1: Get authorization code +auth_url = ( + f"https://www.dropbox.com/oauth2/authorize" + f"?client_id={CLIENT_ID}" + f"&redirect_uri={REDIRECT_URI}" + f"&response_type=code" + f"&token_access_type=offline" # Required: key parameter to get refresh_token +) +print("👉 Please visit the following link to authorize:\n") +print(auth_url) +webbrowser.open(auth_url) +auth_code = input("\n✅ After authorization, paste the code after ?code= in the redirected URL here:\n> ").strip() +# Step 2: Exchange for access_token + refresh_token +token_url = "https://api.dropboxapi.com/oauth2/token" +data = { + 'code': auth_code, + 'grant_type': 'authorization_code', + 'client_id': CLIENT_ID, + 'client_secret': CLIENT_SECRET, + 'redirect_uri': REDIRECT_URI +} +response = requests.post(token_url, data=data) +response.raise_for_status() +tokens = response.json() +# ✅ Only output the refresh token +print("\n🎉 Success! Your Dropbox refresh_token is:\n") +print(tokens.get("refresh_token")) +``` + +## Root folder file_id + +**Empty is the root directory**:display all files +**Single folder ID**:Enter the folder you need to display, copy the top link to fill in `/home` behind +![](/img/drivers/dropbox/folder_id.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/emby.md b/pages/en/guide/drivers/emby.md new file mode 100644 index 000000000..434ac4215 --- /dev/null +++ b/pages/en/guide/drivers/emby.md @@ -0,0 +1,54 @@ +--- +top: 493 +categories: + - guide + - drivers +--- + +# Emby + +Mount media resources from an Emby server. + +## URL + +Emby server address, for example: `http://127.0.0.1:8086`. + +## ApiKey + +Emby API Key. Use together with `UserID` for API authentication. + +## UserID + +Emby user ID. Used with `ApiKey`. + +## Username + +Emby account username. Used with `Password`. + +## Password + +Emby account password. Used with `Username`. + +## LinkMethod + +Choose link method: + +- `stream`: Streaming link (recommended) +- `download`: Direct download link + +Some Emby servers do not grant download permission. In that case, please use `stream`. + +## Authentication Method + +Use **one** of the following login methods: + +1. `ApiKey + UserID` +2. `Username + Password` + +Do not mix both methods at the same time. + +## Example Configuration + +- URL: `http://127.0.0.1:8086` +- LinkMethod: `stream` +- Authentication: choose one method only diff --git a/pages/en/guide/drivers/febbox.md b/pages/en/guide/drivers/febbox.md new file mode 100644 index 000000000..5e725220d --- /dev/null +++ b/pages/en/guide/drivers/febbox.md @@ -0,0 +1,57 @@ +--- +top: 591 +categories: + - guide + - drivers +--- + +# FebBox + +FebBox:https://www.febbox.com + +- The upload function is currently unavailable. + +## Root folder ID + +Root directory ID, default is `0`. + +Other directory IDs can be viewed in the top link address bar after entering the folder. + +- **https://www.febbox.com/console#/files?parent_id=66889900** + + Then the directory ID is `66889900` + +## `Client_id`、`Client_secret` + +Generate address:**https://www.febbox.com/open/clients** + +- The generated client ID and secret key are filled in in the opposite order to the OpenList, so be careful not to fill them in incorrectly. + + ![](/img/drivers/febbox/febox.png) + +## User IP + +**Optional**, the IP address of the user when downloading, quoting the official description + +> IP address, Optional parameter. Supports IPv6 format. After filling in, the best download server suitable for the IP location will be selected. If not filled in, the requested IP will be used. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/feiji.md b/pages/en/guide/drivers/feiji.md new file mode 100644 index 000000000..63989cb5a --- /dev/null +++ b/pages/en/guide/drivers/feiji.md @@ -0,0 +1,50 @@ +--- +top: 686 +categories: + - guide + - drivers +--- + +# FeiJi Cloud + +**https://feijipan.com/** + + + +::: danger +Feiji Cloud imposes login restrictions on 3rd-party platforms. Attempting to access its service via OpenList may result in your account being banned. Please proceed with caution! +::: + +## Root folder ID + +root folder ID the default is `0`,Other directory ID View the figure below obtaining method +![](/img/drivers/feiji/feiji.png) + +## username、password + +Just fill in your own Feiji Cloud Account Password + +## Known issues + +The file size returned by iLanZou is in Kilo Bytes, not Bytes. Therefore, you cannot accurately determine if a file has been modified based on its size. Please pay attention to the configuration of your sync software. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/ftp.md b/pages/en/guide/drivers/ftp.md new file mode 100644 index 000000000..d4aefa2af --- /dev/null +++ b/pages/en/guide/drivers/ftp.md @@ -0,0 +1,47 @@ +--- +top: 898 +categories: + - guide + - drivers +--- + +# FTP + +### Address + +FTP address, need contains port. + +### Username + +FTP username + +### Password + +FTP password + +### Root folder path + +root folder , default `/`, same as local storage. + +## Enter directory before listing + +Whether to change into the target directory before listing files. Some FTP servers do not support listing files with a path argument directly. Enable this option to first `cd` into the directory and then `ls`, which can resolve such compatibility issues. Default: `false`. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/github.md b/pages/en/guide/drivers/github.md new file mode 100644 index 000000000..587366ca6 --- /dev/null +++ b/pages/en/guide/drivers/github.md @@ -0,0 +1,125 @@ +--- +top: 399 +categories: + - guide + - drivers +--- + +# GitHub API + +::: tip +GitHub API does not support operating files larger than 100MiB and folders containing more than 100,000 sub-items. + +All `.gitkeep` files will be managed by the driver and will be invisible to the user. + +Operations on submodules are currently not supported. +::: + +## Token + +1. Log in GitHub and visit . + +2. Select `Generate new token (Classic)` + + ![](/img/drivers/github/1.png) + +3. Fill the information. At least, select **repo**. And click `Generate token`. + + ![](/img/drivers/github/2.png) + +4. Copy the token and paste it into the `Token` field. + + ![](/img/drivers/github/3.png) + +## Owner + +The owner of the repository. For example, if the URL of the repository is `github.com/AAA/BBB`, fill in `AAA` here. + +## Repo + +The name of the repository. For example, if the URL of the repository is `github.com/AAA/BBB`, fill in `BBB` here. + +## Ref + +A branch, a tag or a commit SHA. Only when a branch name is filled in is the driver writable. It defaults to the default branch if not filled. + +## Gh Proxy + +Used to speed up downloading + +- Download the content of `Raw` and `archive` and change it to `https://gh-proxy.com/raw.githubusercontent.com` + +``` +https://gh-proxy.com/raw.githubusercontent.com +``` + +``` +https://ghfast.top/raw.githubusercontent.com +``` + +``` +https://ghproxy.net/raw.githubusercontent.com +``` + +## GPG private key + +GPG private key, used to provide commit verifications. Verified commits will be marked with a green "Verified" label on the GitHub webpage. Use the following command to obtain it. Leave blank if verifications are not needed. + +```bash +gpg --export-secret-key -a +``` + +## GPG key passphrase + +Passphrase to the GPG private key. Leave blank if there is none. + +## Committer name + +Custom committer name, defaulting to the owner's of the token when not filled. + +## Committer email + +Custom committer name, defaulting to the owner's of the token when not filled. +Required when "Committer name" is filled. Required to be empty when "Committer name" is empty. + +## Author name + +Custom author name, defaulting to the owner's of the token when not filled. + +## Author email + +Custom author name, defaulting to the owner's of the token when not filled. +Required when "Author name" is filled. Required to be empty when "Author name" is empty. + +## commit message + +Custom commit message, supporting the following template variables: + +- `UserName`: the user operating the file. +- `ObjName`: the name of the file (or the folder) operated. +- `ObjPath`: the path to the file (or the folder) operated. +- `ParentName`: the name of the parent folder of the file (or the folder) operated. +- `ParentPath`: the path to the parent folder of the file (or the folder) operated. +- `TargetName`: (when renaming) new name. (when copying and moving) the target folder name. +- `TargetPath`: (when renaming) new path. (when copying and moving) the target path. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/github_releases.md b/pages/en/guide/drivers/github_releases.md new file mode 100644 index 000000000..4eda6ee0f --- /dev/null +++ b/pages/en/guide/drivers/github_releases.md @@ -0,0 +1,202 @@ +--- +top: 398 +categories: + - guide + - drivers +--- + +# GitHub Releases + +## Overview + +GitHub Releases driver allows you to mount GitHub repository releases as a file system, enabling direct access to release assets through a familiar directory structure. + +## API Rate Limits + +::: warning Rate Limits + +- **Unauthenticated requests**: 60 requests per hour +- **Authenticated requests** (with token): 5,000 requests per hour + +We recommend using a personal access token to avoid hitting rate limits. +::: + +## Repository Configuration + +### Basic Usage + +**Single Repository Mount** + +Mount a single repository to the root directory: + +```text +OpenListTeam/OpenList +``` + +This is equivalent to: + +```text +/:OpenListTeam/OpenList +``` + +### Multiple Repositories + +**Mount to Subdirectories** + +You can mount multiple repositories to different subdirectories: + +```text +/openlist-gh:OpenListTeam/OpenList +/openlist-frontend-gh:OpenListTeam/OpenList-Frontend +``` + +The leading `/` is optional: + +```text +openlist-gh:OpenListTeam/OpenList +openlist-frontend-gh:OpenListTeam/OpenList-Frontend +``` + +## Configuration Options + +### Personal Access Token + +**When to use:** + +- Required for accessing private repositories +- Recommended to avoid rate limiting issues + +**How to get:** + +1. Log in to GitHub +2. Visit: https://github.com/settings/tokens +3. Generate a new token with appropriate permissions + +### Show All Versions + +**Disabled (default):** + +``` +openlist/ +├── openlist-linux-amd64.tar.gz +└── openlist-windows-amd64.zip +``` + +**Enabled:** + +``` +openlist/ +├── v3.41.0/ +│ ├── openlist-linux-amd64.tar.gz +│ └── openlist-windows-amd64.zip +├── v3.40.0/ +│ ├── openlist-linux-amd64.tar.gz +│ └── openlist-windows-amd64.zip +└── v3.39.4/ + ├── openlist-linux-amd64.tar.gz + └── openlist-windows-amd64.zip +``` + +When enabled, all available release versions are displayed in separate directories. + +### Pagination Control (Show All Versions) + +When "Show All Versions" is enabled, you can control the number of releases fetched via pagination: + +- **`per_page`**: Number of releases per page (default: `30`, max: `100`) +- **`max_page`**: Maximum number of pages to fetch (`0` means unlimited) + +These settings help you avoid hitting API rate limits when repositories have a large number of releases. + +**Example:** + +To fetch at most 2 pages with 50 releases per page (up to 100 releases total): + +```text +per_page = 50 +max_page = 2 +``` + +::: tip +A single unauthenticated request counts as 1 toward the 60 requests/hour limit. With `per_page=100` and `max_page=1`, fetching releases for a single repository costs only 1 request. +::: + +### Show README Files + +**Disabled (default):** + +``` +openlist/ +├── openlist-linux-amd64.tar.gz +└── openlist-windows-amd64.zip +``` + +**Enabled:** + +``` +openlist/ +├── v3.41.0/ +│ ├── openlist-linux-amd64.tar.gz +│ └── openlist-windows-amd64.zip +├── v3.40.0/ +│ ├── openlist-linux-amd64.tar.gz +│ └── openlist-windows-amd64.zip +├── LICENSE +├── README.md +└── README_cn.md +``` + +::: tip +When enabled, README and LICENSE files from the repository are displayed alongside releases. However, folder size and modification time information will not be shown. +::: + +## GitHub Proxy Settings + +Use GitHub proxy services to accelerate downloads in regions with limited GitHub access. + +**Configuration:** +Replace the GitHub domain with a proxy service URL: + +```text +https://gh-proxy.com/https://github.com +``` + +**Available Proxy Services:** + +| Service | URL | +| ----------- | ----------------------------------------- | +| GH-Proxy | `https://gh-proxy.com/https://github.com` | +| GHProxy.net | `https://ghproxy.net/https://github.com` | +| GHFast | `https://ghfast.top/https://github.com` | + +Example: + +``` +Before: https://github.com/owner/repo/releases/download/v1.0/file.zip +After: https://ghproxy.net/https://github.com/owner/repo/releases/download/v1.0/file.zip +``` + +::: warning +Proxy services are third-party and may have varying availability and performance. +::: + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/google_drive.md b/pages/en/guide/drivers/google_drive.md new file mode 100644 index 000000000..00787d815 --- /dev/null +++ b/pages/en/guide/drivers/google_drive.md @@ -0,0 +1,179 @@ +--- +top: 597 +categories: + - guide + - drivers +--- + +# Google Drive + +Official website: https://drive.google.com/ + +- Supports Team Drive (Enter the Team Drive directory ID for the Root Folder ID) + +## 1. Prerequisites + +1. The machine on which the OpenList service is deployed must be able to connect to Google Drive. + +2. The Google Drive API service must be enabled. For details on how to enable it, please refer to [Enabling the Google Drive API](#_2-1-enable-google-drive-api). + + > For detailed steps, see [Google Workspace > Google Drive > Quick Start Guide](https://developers.google.com/workspace/drive/api/quickstart/js). + +## 2. Preparation + +### 2.1. Enable Google Drive API + +1. On the [Guide Page](https://developers.google.com/workspace/drive/api/quickstart/js), locate the `Enable the API` button and click to proceed to the quick activation interface. Follow the instructions and confirm the settings to complete the activation process. + +2. You can also visit the [Google Drive API Management Interface](https://console.cloud.google.com/apis/library/drive.googleapis.com). Find the `Enable` button at the top and click it; wait for the activation to complete. + +3. `Quotas and System Limits Management`(optional): Switch to the `Quotas and System Limits` tab and set appropriate quotas and limits based on your usage and requirements. + +### 2.2. Creating an OAuth Client (Optional; not required if using OpenList, a public welfare server, or a self-built server) + +1. On the [Credentials Page](https://console.cloud.google.com/apis/credentials?hl=en), click the `Create Credentials` dropdown menu and select and click `OAuth client ID`. + > If prompted with `To create an OAuth client ID, you must first configure the consent screen`, click the `Configure consent screen` button, and complete the creation of the `Brand Information` on the subsequent page, selecting `External` for the `Audience`. After completion, return to the Credentials page to continue creating the OAuth client ID. + > ![google-oauth-00](/img/drivers/google/google-oauth-eng-00.png) +2. In the pop-up window, select `Web Application` and enter the name of your application type. +3. For the `Authorized redirect URIs`, enter `https://api.oplist.org/googleui/callback`. If you are using a self-built service or a non-profit organization, please enter the corresponding domain name, for example, `https://your-domain.com/googleui/callback`. +4. Click the `Create` button to complete the creation of the OAuth client. Note: Make sure to copy and save the `Client ID` and `Client Secret`; you will need them later on. + + ![google-oauth-01](/img/drivers/google/google-oauth-eng-01.png) + +5. Add yourself as a test user: On the left side, find the `Audience` menu and click to enter it. Then, locate `Test Users` and click the `+ Add users` button. Enter your Google account email address and click `Save`. + + ![google-oauth-02](/img/drivers/google/google-oauth-eng-02.png) + +6. After completing the testing, you can publish your application: On the left side, find the `Audience` menu and click to enter it. Then, locate `Publication Status` and click the `Publish app` button. Confirm the publication to complete the application release process. + + ![google-oauth-03](/img/drivers/google/google-oauth-eng-03.png) + +### 2.3. Get `Access Token` and `Refresh Token` + +1. Open the [OpenList Google Authorization Page](https://api.oplist.org/). **⚠️ If you are using a public or self-hosted server, please proceed with that server.** + +2. In the dropdown menu on the page, select `GoogleDrive Login`. + + ![google-00-l](/img/drivers/google/google-00-l.png#light) + ![google-00-d](/img/drivers/google/google-00-d.png#dark) + +3. If you created an OAuth Client ID in the previous steps, please enter the Client ID and Client Secrets in the input fields below. + + ![google-01-01-l](/img/drivers/google/google-01-01-l.png#light) + ![google-01-01-d](/img/drivers/google/google-01-01-d.png#dark) + + Otherwise, check the box ☑️ to use the API provided by OpenList. **⚠️ If you are using a public or self-hosted server, the built-in Client ID and Client Secrets of that server will be used.** + + ![google-01-02-l](/img/drivers/google/google-01-02-l.png#light) + ![google-01-02-d](/img/drivers/google/google-01-02-d.png#dark) + +4. Click the `Get Authorization` button. The system will redirect you to the Google Authorization page. Log in to your Google account and authorize OpenList to access your Google Drive. + + ![google-02](/img/drivers/google/google-02.png) + +5. If a message appears stating `This app has not been verified by Google`, click `Advanced` and then click `Go to oplist.org (insecure)` to continue. (If you are using a public or self-hosted server, the domain name here should match the actual domain name of the service; please verify this carefully.) + + ![google-03](/img/drivers/google/google-03.png) + +6. Pay attention to the permission information on the authorization page (within the red box in the image below). Make sure that the permission granted is only for accessing your Google Drive files. If you find that the permission includes access to something other than just your Google Drive files in the public service, please report an issue at [OpenListTeam/OpenList-Docs/issues](https://github.com/OpenListTeam/OpenList-Docs/issues). + + ![google-04](/img/drivers/google/google-04.png) + +7. After authorization is successful, the system will provide you with a `Refresh Token` and an `Access Token`. Please copy and save these tokens, as they will be used in subsequent configurations. + + ![google-05-l](/img/drivers/google/google-05-l.png#light) + ![google-05-d](/img/drivers/google/google-05-d.png#dark) + +## 3. Add Google Drive in OpenList + +### 3.1. Configuration + +#### 3.1.1. Root Folder ID + +Similar to Aliyun Drive, it is the last string of the official website URL, such as: + +![google](/img/drivers/google/googledrive-dir.png) + +### 3.2. Start Adding + +1. Open the management interface of OpenList and click on `Storage` in the left menu. + +2. On the Storage List page, click the `Add Storage` button in the top right corner. + +3. Select `Google Drive` as the drive. + + ![google-06-l](/img/drivers/google/google-06-l.png#light) + ![google-06-d](/img/drivers/google/google-06-d.png#dark) + +4. Enter the mount path, for example: `google-drive`. + +5. In the `Root Folder ID` field, enter the root folder ID obtained earlier; if you are using the root directory, enter `root`. + +6. In the `Refresh Token` field, enter the refresh token obtained earlier (if not available, refer to [Preparing for Integration](#_2-preparation). + +7. If you are using the OAuth client ID and key provided by OpenList (or a public welfare server/self-built server), follow steps 7.1 and 7.2: + + 7.1. Check the `Use online API` option to indicate that you want to use the online API provided by OpenList. + + 7.2. Enter `https://api.oplist.org/googleui/renewapi` as the API URL; if it’s a public welfare server/self-built server, enter the corresponding server address. + + ![google-07-d](/img/drivers/google/google-07-d.png#dark) + ![google-07-l](/img/drivers/google/google-07-l.png#light) + +8. If you are using your own OAuth client ID and key, follow steps 8.1 and 8.2: + + 8.1. Uncheck the `Use online API` option to indicate that you are using your own OAuth client ID and key. + + 8.2. Enter your OAuth client ID in the `Client id` field and your OAuth Client secrets in the `Client secret` field. + + ![google-08-l](/img/drivers/google/google-08-l.png#light) + ![google-08-d](/img/drivers/google/google-08-d.png#dark) + +9. Click the `Add` button to complete the addition of Google Drive. + +## 4. About `Use online api` option + +### 4.1. AccessToken refresh method with own keys + +```mermaid +sequenceDiagram + participant OpenList + participant GoogleDrive + OpenList->>GoogleDrive: Provide refresh token + built-in client ID and secret + GoogleDrive->>OpenList: Return new access token + refresh token +``` + +### 4.2. AccessToken refresh method without own keys + +```mermaid +--- +title: How to refresh AccessToken via OnlineAPI? +--- +sequenceDiagram + participant OpenList + participant OnlineAPI + participant GoogleDrive + OpenList->>OnlineAPI: Provide refresh token + OnlineAPI->>GoogleDrive: Provide refresh token + built-in client ID and secret + GoogleDrive->>OnlineAPI: Return new access token + refresh token + OnlineAPI->>OpenList: Return new access token + refresh token +``` + +## 5. The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[Local Proxy]:::someclass==Default===>a2[User Device] + classDef someclass fill:#f96 + b1[Proxy URL]-.Alternative.->a2[User Device] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/google_photos.md b/pages/en/guide/drivers/google_photos.md new file mode 100644 index 000000000..292229af6 --- /dev/null +++ b/pages/en/guide/drivers/google_photos.md @@ -0,0 +1,73 @@ +--- +top: 596 +categories: + - guide + - drivers +--- + +# Google Photos + +::: tip +Due to the adjustments made by Google Photos to its Library API in April this year (https://developers.google.com/photos/support/updates?hl=en), after successfully mounting Google Photos to openlist, you can actually only view the photos uploaded through openlist. You can no longer view the photos uploaded by users via the official Google Photos app or website as you could before. +::: + +## Root folder file_id + +The root directory is root, and other directory IDs are unknown (~~It seems that a separate album cannot be mounted~~ It has not been resolved yet [**Reference**](https://github.com/alist-org/alist/discussions/3264#discussioncomment-4874536)) + +## Get client_id, client_secret + +Get the tool https://api.oplist.org + +How to get your own `client_id (client ID) client_secret (client secret key)` Go to [**Google Drive**](./google_drive.md) which article + +::: tip + +Tip This app has not been verified by Google + +You have been authorized to use an app that is currently in beta. Do not proceed unless you know the developer who invited you. + +Let's click **Continue** on the left + +Remember to check this option, otherwise an error will be reported, as explained in the following precautions +::: + +It is more convenient now. In fact, when obtaining Google cloud disk authorization, select the album permission at the same time, and the token obtained at that time can also be used to mount the album~ kill two birds with one stone + +Remember to enable **`Photos Library API`** + +- https://console.cloud.google.com/apis/library/photoslibrary.googleapis.com + +According to [above](https://api.oplist.org) obtained **client ID** and **secret key** and **refresh_token** + +We fill in **https://api.oplist.org** to get a single catalog ID for each of our Google Photos + +- Root file ID (that is, the album ID): default root, display all, if you only want to display a single album, you can fill in the album ID you want to display +- After we fill in, there will also be an **`access_token`** below. This can also be filled in the OpenList background refresh token option and used. The refresh token obtained at the beginning can be used for both of them- [Explanation](https://github.com/alist-org/alist/discussions/3264#discussioncomment-5051171) + +## Two notes: + +1. failed get objs: failed to list objs: Request had insufficient authentication scopes.: [] + - It's because you didn't check the album permission (see the picture mentioned above) `view and organize the content in your Google Photos media library, and upload content to the media library when obtaining authorization. Learn more` + - You get the `access_token` and the `refresh_token` without declearing the scope of `https://www.googleapis.com/auth/photoslibrary` +2. failed get objs: failed to list objs: Photos Library API has not been used in project ... before or it is disabled. Enable it by visiting then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.: [] + - because **`Photos Library API`** is not enabled in your project, Google Photos API + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/guangyapan.md b/pages/en/guide/drivers/guangyapan.md new file mode 100644 index 000000000..aaa5a7425 --- /dev/null +++ b/pages/en/guide/drivers/guangyapan.md @@ -0,0 +1,89 @@ +--- +top: 701 +categories: + - guide + - drivers +--- + +# GuangYaPan + + + +:::: warning +This driver uses a **two-stage SMS login**. You need to provide a valid `client_id` before login. After completing the SMS login, `access_token` and `refresh_token` are saved automatically, and you can log in again with just `access_token` or `refresh_token`. +:::: + +## 1. Add in OpenList + +### Client ID + +**Required**. Fill in the `client_id` for the GuangYaPan API. This field must be provided, otherwise storage initialization will fail. + +### Phone Number + +The phone number used for SMS login, e.g. `+86 13800000000`. + +### Captcha Token + +Captcha token required by `/v1/auth/verification`. Leave it empty if no captcha is triggered. + +### Send Code + +Set it to `true` and save to send the SMS code. It auto-resets to `false` after sending. + +### Verify Code + +The SMS verification code you received. Fill it in and save to finish the login. + +### Verification ID + +Auto-generated after sending the SMS code. Do not edit it manually. + +### Access Token + +Bearer access token. It is saved automatically after SMS login. If you already have a valid `access_token` or `refresh_token`, you can fill it directly instead of doing the SMS login. + +### Refresh Token + +Refresh token for auto-login and auto-refresh. Saved automatically after SMS login. + +### Root Folder Path + +Full path in the GuangYaPan cloud drive, e.g. `/Movies/Anime`. Leave it empty to use the root directory. + +### Device ID + +Optional custom device id (32 hex chars). Auto-generated when empty. + +### Device Sign + +Optional custom `X-Device-Sign` header. Generated from `device_id` when empty. + +### Page Size / Order By / Sort Type + +- `Page Size`: file list page size, default `100`. +- `Order By`: sort field used by the file list, options `0,1,2,3,4`, default `3`. +- `Sort Type`: sort direction used by the file list, options `0,1`, default `1`. + +## 2. SMS Login Steps + +1. Fill in `client_id` and `phone_number` (and `captcha_token` if needed). +2. Set `send_code` to `true`, then save. The storage will enter a "SMS sent" status and the `verification_id` is generated automatically. +3. Fill in the received `verify_code`, then save again to finish the login. The `access_token` and `refresh_token` are saved automatically. + +## 3. Offline Download + +GuangYaPan supports calling its own offline download function from OpenList. + +1. Mount a GuangYaPan storage. +2. In the backend **Settings** → **Other**, set the GuangYaPan temporary directory (choose any folder of this account). +3. Go back to the frontend, enter the target folder, and choose **GuangYaPan** in the offline download option at the lower right corner. + +- Supports adding offline tasks via URL (e.g. `http`, `magnet` links). + +## 4. Precautions + +- `client_id` is required, otherwise storage initialization will fail. +- The `verify_code` is one-time use and will be cleared after a successful login. +- The driver does not support overwriting uploads (same-name files will not overwrite). +- Login priority: `access_token` → `refresh_token` → SMS login. diff --git a/pages/en/guide/drivers/halalcloud.md b/pages/en/guide/drivers/halalcloud.md new file mode 100644 index 000000000..f66526de6 --- /dev/null +++ b/pages/en/guide/drivers/halalcloud.md @@ -0,0 +1,56 @@ +--- +top: 679 +categories: + - guide + - drivers +--- + +# halalcloud + +- `6盘(halalcloud)` Official website:[https://2dland.cn](https://2dland.cn/) + - Cloud Login:https://drive.2dland.cn +- Official announcement, document address:https://2dland.yuque.com/r/organizations/homepage + +## Root folder file_id + +Top address bar path,Root folder file_id is:`/` +Subfolder: `/A folder/C folder/C folder` + +## Fill in the example + +On the HalalCloud (6 盘) website, go to `User Center` and navigate to `Authorization Management`. Enter your HalalCloud account password to verify your identity. + +Create a new authorization (you can name it anything you like). Click `Confirm`, then copy and save the `Client ID` and `Client Secret`. + +![halalcloud_add_authorization](/img/drivers/halalcloud/halalcloud_add_authorization.png) + +In the OpenList admin panel, go to `Storage` and add a new driver. Select `HalalCloudOpen`, and enter the `Client ID` and `Client Secret` obtained from the previous step. + +## Other parameters + +- `Upload thread`: Upload threads (Default: 3, Range: 1-32) + +- `Host`: (Provided by default, no input required) + +- `WebDAV Policy`: Default is `302 Redirect`. Switch to `Local Proxy` if you encounter any issues. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/ilanzou.md b/pages/en/guide/drivers/ilanzou.md new file mode 100644 index 000000000..ce47454b9 --- /dev/null +++ b/pages/en/guide/drivers/ilanzou.md @@ -0,0 +1,46 @@ +--- +top: 687 +categories: + - guide + - drivers +--- + +# iLanZou + +**https://ilanzou.com** + + + +## Root folder ID + +root folder ID the default is `0`,Other directory ID View the figure below obtaining method +![LanZoufolder_id](/img/drivers/lanzou/ilanzou_folder.png) + +## username、password + +Just fill in your own NewLanzou Cloud Account Password + +## Known issues + +The file size returned by iLanZou is in Kilo Bytes, not Bytes. Therefore, you cannot accurately determine if a file has been modified based on its size. Please pay attention to the configuration of your sync software. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/index.md b/pages/en/guide/drivers/index.md new file mode 100644 index 000000000..e69de29bb diff --git a/pages/en/guide/drivers/kodbox.md b/pages/en/guide/drivers/kodbox.md new file mode 100644 index 000000000..e5f2ed091 --- /dev/null +++ b/pages/en/guide/drivers/kodbox.md @@ -0,0 +1,55 @@ +--- +top: 496 +categories: + - guide + - drivers +--- + +# KodBox + +Use this driver to mount a KodBox netdisk space to OpenList. + +## Root folder path + +Suppose you have a network disk space named `My Files`. If you remount the contents of the network disk space, you need to obtain the path corresponding to `My File`; if you re-display a name in the network disk space for For the directory of `abc`, you must obtain the path corresponding to `My File/abc`, and so on. + +Example: How to get the path of KodBox network disk space `My File`, the result path is `{source:5}` + +Open the KodBox network disk space in the browser. In the console mode, you can see the path corresponding to the network disk space. Do not leave it blank. +![kodbox_root_folder_path](/img/drivers/kodbox/kodbox_root_folder_path_en.jpg) + +## Address + +Your KodBox server address, e.g. + +- `https://kodcloud.cc` +- `http://192.168.1.24:8000` + +## Username + +The email or username used to log in to your KodBox server. + +## Password + +The password for your email or username. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/lanzou.md b/pages/en/guide/drivers/lanzou.md new file mode 100644 index 000000000..14c59afe1 --- /dev/null +++ b/pages/en/guide/drivers/lanzou.md @@ -0,0 +1,94 @@ +--- +top: 688 +categories: + - guide + - drivers +--- + +# LanZou Cloud + + + +Lanzuo Network Disk has three types of **modes (types)**, which are **Accounts**, **Cookie** and **Url** + +- **Account**: You can display all the files of your own cloud disk, you only need to fill in the account password, which will automatically help you refresh the cookie + +- **Cookie**: All files in the network disk can be displayed, but the cookie of the network disk needs to be provided + +- **Url**: only display the content in the link (you can mount it without providing a cookie) + +::: tip + +It is recommended to use the **Account** method to mount + +- Cookie mode needs to manually update Cookie + +::: + +## root folder ID + +Lanzuo cloud disk root directory ID, the default is `-1`,If you use `Url`Type to mount the root folder ID, please see the third item of [Fill in instructions](#fill-in-instructions) + +- **Additional Folder ID** + - Two ways to obtain (see the picture below) + + ![](/img/drivers/lanzou/lanzou2.gif) + +## Account + +Just fill in your own Lanzou Cloud Account Password + +## Cookie + +Log in to your [**Lanzuo Cloud Disk**](https://pc.woozooo.com/) account, then F12 to open the developer mode, you can find `Cookie`, there is no specific one, such as the left side of the picture Just click one and there will be `Cookie` in the blue part of the right picture. +![lanzou](/img/drivers/lanzou/lanzou1.png) + +## Share link and share password + +- **Share link**: Randomly generate a share link in the Lanzuo cloud disk + - Small tip: **lanzouX** in the link, the last default **X** content sometimes cannot be opened in some areas, you can try to change it yourself, for example, change to i, x, u, t, etc. Anyway, try it out for yourself. +- **Share password**: the password for the link + +## Repair file info + +If you need **WebDav** service, you need to open it + +## error message + +Use cookie type or account type to add prompt `not find file page param` error because lanzou changed the domain name + +- Solution: It is recommended to use account type to add + - Change the sharing link to `https://wwop.lanzoul.com` and save it. If it still doesn't work, refresh the lower right corner. + - lanzoul will also make changes in the future + +## Fill in instructions + +1. **Acount mode**: Type selects `account`, fill in` account` and `password` options, and the `root folder ID` (the default is all files of the root directory) +2. **Cookie mode**: Type selects `Cookie`, fill in` Cookie`, and the `root folder ID` (the default is all files of the root directory) +3. **URL mode**: Type selects `Url`, fill in` ShareUrl` and `root folder ID` and`Share Password (if there is a password)` + - The sharing link is :point_right: https://xxx.lanzou.com/aaabbbccc :point_left: in this format + - Fill in `ShareUrl`: https://xxx.lanzou.com + - Fill in `root folder ID`: aaabbbccc + - Share Password: if there is a password + ![lanzou](/img/drivers/lanzou/lanzou_add.png) + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/lenovo_nas_share.md b/pages/en/guide/drivers/lenovo_nas_share.md new file mode 100644 index 000000000..c6c9a0476 --- /dev/null +++ b/pages/en/guide/drivers/lenovo_nas_share.md @@ -0,0 +1,59 @@ +--- +top: 494 +categories: + - guide + - drivers +--- + +# Lenovo Nas Share + + + +Need to purchase Lenovo devices **https://pc.lenovo.com.cn** + +## Root Folder ID + +Root Folder ID: Leave it blank + +Subfolder ID: Get as shown in the picture + +![](/img/drivers/lenovonasshare/lenovonasshare_fileid.png) + +## Share ID and Share Password + +Example of share link: https://siot-share.lenovo.com.cn/s/#/eb.3N93ZbJsaAjerjdm4N Extraction code: `e5eu` + +- **Share ID**: Fill in the sharing link and automatically extract the string `eb.3N93ZbJsaAjerjdm4N` at the end of the sharing link +- **Share Password**: The extraction code `e5eu` + +## Host Address + +The default uses the public network: **https://siot-share.lenovo.com.cn** + +(Not recommended) If you are using a local network, you can change it to the internal network address of the Lenovo device: **http://192.168.XX.XX** + +## Show Root Folder + +If unchecked and `Share ID` is empty, the folder ID of the first-level folder is automatically filled in. +Taking the above picture as an example, the contents of the `OpenList` folder are directly displayed without displaying the `OpenList` folder. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/local.md b/pages/en/guide/drivers/local.md new file mode 100644 index 000000000..c18af02d6 --- /dev/null +++ b/pages/en/guide/drivers/local.md @@ -0,0 +1,65 @@ +--- +top: 999 +categories: + - guide + - drivers +--- + +# Local storage + +Support mounting the directory of the local machine. + +## Root folder path + +The path of folder you wanted to mount. For example: + +- Linux: `/root` +- Windows: `C:` + +## Local storage video thumbnail + +You need to use the `ffmpeg` tool to add. + +## Local storage PDF thumbnail on macOS + +On macOS, the Local storage driver can generate thumbnails from the first page of PDF files using the system Quick Look tool. + +To enable this feature: + +1. Enable `Thumbnail`. +2. Enable `PDF thumbnail`. +3. It is recommended to configure `Thumb cache folder` to avoid rendering the same PDF repeatedly. + +This feature is disabled by default and is only available when OpenList runs on macOS. Rendering is performed on cache misses and may consume additional CPU and memory. + +## Recycle bin path + +path to recycle bin, delete permanently if empty or keep 'delete permanently' + +If you fill in this path, you will move the file into the folder when deleting the local storage file, so that you have a chance to regret it. + +The method of filling in the above -mentioned mounting path is different from different system filling methods. + +If you don’t know if you fill in it correctly, you can test it yourself first and then use the production environment to use it yourself. + +- Linux: `/root` +- Windows: `C:` + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/mediafire.md b/pages/en/guide/drivers/mediafire.md new file mode 100644 index 000000000..a72652628 --- /dev/null +++ b/pages/en/guide/drivers/mediafire.md @@ -0,0 +1,30 @@ +--- +top: 520 +categories: + - guide + - drivers +--- + +# MediaFire + +Mount [MediaFire](https://www.mediafire.com/) cloud storage. + +## Root folder path + +The default root directory for the path to mount mediafire, default is `/`. + +## Session Token + +Session Token + +Open the browser developer tools, visit [mediafire.com](https://www.mediafire.com/), log in to your account, go to the `Network` tab, open `get_session_token.php`, find `session_token` in the `Response`, and copy its value to fill in. + +![Mediafire Session Token](/img/drivers/mediafire/mediafire-sessiontoken.png) + +## Cookie + +Web Cookie + +Open the browser developer tools, visit [mediafire.com](https://www.mediafire.com/), log in to your account, go to the `Network` tab, open `get_session_token.php`, find `Cookie` in the `Header`, and copy its value to fill in. + +![Mediafire Cookie](/img/drivers/mediafire/mediafire-cookie.png) diff --git a/pages/en/guide/drivers/mediatrack.md b/pages/en/guide/drivers/mediatrack.md new file mode 100644 index 000000000..89eeeaa1e --- /dev/null +++ b/pages/en/guide/drivers/mediatrack.md @@ -0,0 +1,46 @@ +--- +top: 680 +categories: + - guide + - drivers +--- + +# Mediatrack + +**https://app.mediatrack.cn** + +### Access token + +You can get it in request header after logging in +![token](/img/drivers/mediatrack-token.png) + +### Project id + +Get from official website url: +![Project id](/img/drivers/mediatrack-projectid.png) + +### Root folder id + +Get it from the request after logging in +![id](/img/drivers/mediatrack-rootid.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/mega.md b/pages/en/guide/drivers/mega.md new file mode 100644 index 000000000..1d0f1de52 --- /dev/null +++ b/pages/en/guide/drivers/mega.md @@ -0,0 +1,64 @@ +--- +top: 592 +categories: + - guide + - drivers +--- + +# MEGA Disk + +::: warning +Due to MEGA requires decryption to be performed on the OpenList server backend, **using proxy URL** with this driver cannot avoid server traffic consumption. +::: + +MEGA official website: **https://mega.nz/login** + +### Mail + +MEGA login account + +### password + +login password + +## 2FA + +Non -filling options,If you open 2FA, you need to fill in it,If you don’t use 2FA, you don’t need to fill in it + +### Two fa secret + +**https://mega.nz/fm/account/security/two-factor-authentication** +Click to enable `Enable two-factor authentication (2FA)` and then see a QR code, there is a string of letters below the QR code, This is `Two fa secret`, please record before scanning the code and do not lose it, Should the `Two-factor authentication (2FA) secret` be lost, you can unbinding and then retrieve a new 2FA QR code for setup. +![](/img/drivers/mega/mega_2fa.png) + +### Two fa code + +To enable 2-factor authentication, you need to install a app that support 2FA (such as [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2), [Microsoft Authenticator](https://support.microsoft.com/en-us/account-billing/download-and-install-the-microsoft-authenticator-app-351498fc-850a-45da-b7b6-27e523b8702a)), on your phone. +Then login to OpenList manage and goto `Profile` page, click `Enable 2FA` button, scan the QR code with your 2FA app, and enter the code generated by your 2FA app. +Scan the QR appearing on the screen. At the same time, this QR code is also [two-fa-secret](#two-fa-secret) +![](/img/drivers/mega/mega_2fa_code.png) +
+ +## Fill in example: + +![](/img/drivers/mega/mega_add.png) +
+ +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/misskey.md b/pages/en/guide/drivers/misskey.md new file mode 100644 index 000000000..9788bcda3 --- /dev/null +++ b/pages/en/guide/drivers/misskey.md @@ -0,0 +1,17 @@ +--- +top: 588 +categories: + - guide + - drivers +--- + +# Misskey Drive + +## Instance URL + +The URL of your Misskey instance, whether self-hosted or in use. +For example, the official instance at `https://misskey.io` + +## Token + +Visit `https://{misskey-instance}/settings/api` to generate an access token. It requires the `read:drive` and `write:drive` permissions. diff --git a/pages/en/guide/drivers/mopan.md b/pages/en/guide/drivers/mopan.md new file mode 100644 index 000000000..dea6db66c --- /dev/null +++ b/pages/en/guide/drivers/mopan.md @@ -0,0 +1,50 @@ +--- +top: 691 +categories: + - guide + - drivers +--- + +# MoPan + + + +MoPan address:**https://mopan.sc.189.cn/mopan/#/downloadPc** + +- There is no web version, only `Android`, `iOS`, `PC-Win64bit`, `iPad`, and `TV`. + +## Sms code + +Enter the option of the mobile phone number and password when the first addition, and then enter the `SMS Code` input `send`, and then click Save to send it to you. + +## Root folder ID + +Do not fill in this option, it will automatically fill into the root directory + +- Due to encrypted requests, an appropriate method for obtaining folder IDs has not yet been found + +### Tips + +1. `root folder ID`,`equipment information`does not need to be filled in, will automatically help you fill +2. If you enter the send in [SMS Code] (#SMS-Code), it is found that it has been saved,Please click Edit to enter the verification code received + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/onedrive.md b/pages/en/guide/drivers/onedrive.md new file mode 100644 index 000000000..e0a92da35 --- /dev/null +++ b/pages/en/guide/drivers/onedrive.md @@ -0,0 +1,146 @@ +--- +top: 599 +categories: + - guide + - drivers +--- + +# OneDrive / Share + +:::tip + +- If you have global administrator permissions for a non-home edition, you can use the [OneDrive APP](onedrive_app.md) driver. +- If your account does not support the API, (for example, the school account has not verified the administrator, or the administrator has disabled the API), then you can also mount it through webdav. For details, see [webdav](webdav.md) + +::: + +## 1. Mounting using Online API's default application + +You do not need to create an application yourself by mounting in this way. + +1. Open and select the corresponding OneDrive version according to your account. + +2. Check "Use parameters provided by OpenList", click "Get Token", then log in to the OneDrive account you want to mount. After authorization, return to the page to get the refresh token. + + ![Online Token Acquisition Tool](/img/drivers/OneDrive/online_api.png) + +3. Go to the storage management interface of OpenList, select the OneDrive driver, check "Use online API", fill in the refresh token and you can mount it. + + ![Backend Configuration Interface](/img/drivers/OneDrive/online_api_config.png) + +## 2. Mounting by creating an application manually + +The application provided by OpenList API may experience issues such as request rate limits due to a high number of users. In this case, you can manually create an application. + +1. Navigate to the corresponding management page based on your account type. + - OneDrive Global:https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade + - OneDrive 21vianet:https://portal.azure.cn/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade + - OneDrive Germany:https://portal.microsoftazure.de/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade + - OneDrive US GOV:https://portal.azure.us/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade + +2. After logging in, select `Register Application`, enter `Name`, and select `Accounts and Individuals in Any Organization Directory` (note that you don't look at the location selection but the text here. Some people may be the middle option, don't select a single Tenant or other options, otherwise it will cause problems when logging in), enter the `Redirect URL` as `https://api.oplist.org/onedrive/callback`, click `Register`, and then you can get the `client_id`. + + ![client_id](/img/drivers/OneDrive/onedrive-register-app.png) + +3. After registering the application, select `Certificate and Password`, click `New Client Password`, enter a string of passwords, select the one with the longest time, and click `Add`. + + (Note: The password entered after adding will disappear, please record the value of client_secret) + + ![client_secret](/img/drivers/OneDrive/onedrive-new-password.png) + +4. Select `API Permissions`, click `MicroSoft Graph`, enter file in the `Select Permissions`, and check `Files.read` (Note: `Files.read` is a read-only minimum permission. The permission in the figure is larger, and the same can be done), click `Update Permission`. + + ![api](/img/drivers/OneDrive/onedrive-update-permission.png) + +5. Fill in the `client_id` and `client_secret` obtained in the previous step into page, click `Get Token`. + +6. Go to OpenList's add storage page, uncheck "Use online API", and fill in the obtained `client_id`, `client_secret`, `Callback URL`, and `Refresh Token` in OpenList. + +## Parameters + +### Sharepoint site_id + +If you need to mount SharePoint, after completing the above steps, there will be an input field for the site address below the refresh token display. Enter the site address, click to get the `site_id`, and then fill the obtained information into the `site_id` field on the OpenList add storage page. Make sure `Is sharepoint` is enabled. + +### Root folder path + +The default is `/`, if you need to customize, just fill in the path, starting from the root path, the same as the local path, such as `/test` + +### Chunk size + +Upload chunk size (MiB). The default is `5`, which means `5 * 1024 * 1024 = 5,242,880` bytes. Make sure to use a size that is a multiple of 320 KiB (`327,680` bytes). + +### Custom host + +Custom accelerated download link. This is the domain name of your reverse-proxied OneDrive download API (for example, for personal accounts: `my.microsoftpersonalcontent.com`). Only domain replacement is supported here, not path replacement. + +::: warning + +- Only hostname replacement is supported, not path. +- Be sure to properly isolate and only reverse proxy your own paths, otherwise your Cloudflare account may be banned! + +::: + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 3. OneDrive Share Url + + + +![api](/img/drivers/onedrive_app/od_share_add.png) + +### Url + +The sharing link is the same as the example below and can be mounted. It can be obtained from E3, E5, A1, and A1P. + +```html +https://connecthkuhk-my.sharepoint.com/:f:/g/personal/jhyang13_connect_hku_hk/EsEgHtGOWbJImxop6tF15FIBIH-ihrjuDclbrbmwWfY_RA?e=s6fitN +``` + +If it is OneDrive personal version, it will not work. The link is as follows + +```html +https://onedrive.live.com/?cid=64EA5FCC7735E8C6&id=64EA5FCC7735E8C6%2117289 +``` + +### Password + +It is the extraction code. If you have it, write it. If you don’t have it, don’t fill it in. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/onedrive_app.md b/pages/en/guide/drivers/onedrive_app.md new file mode 100644 index 000000000..dc418747e --- /dev/null +++ b/pages/en/guide/drivers/onedrive_app.md @@ -0,0 +1,173 @@ +--- +top: 598 +categories: + - guide + - drivers +--- + +# OneDrive APP + +## Benefits and Precautions + +1. The advantage is that the administrator is only authorized to log in once, and the same is used when adding parameters later. When adding, everything is the same except for the mailbox. + - For example, E5 has 25 5T accounts. After you log in and initialize in advance, you can directly use the parameters before the CV. You don’t have to get the refresh token every time as before, and it will expire after a long time + - A1, A1P, etc. can distribute more accounts (500 - **unlimited**) +2. After the administrator authorizes in this way, he can view the account of each member in the organization at will, which will leak his own files + - It is recommended to apply for OneDrive E5 to get better + +::: tip +It is not applicable to the home version. If you are in the configuration process, it does not match the options that appear in the tutorial. It may not be supported +::: + +First log in with your personal or organizational account + + + +Login to get our `tenant ID` + +![onedriveapp](/img/drivers/onedrive_app/onedrive_app0.png) + +To log in to OneDrive (Operated by 21Vianet), please use this link + + + +## Create process + +::: danger Read the precautions carefully +Please read carefully the following **`serial numbers , view in sequence`** , if there is any error, please feedback in time +::: + +1. Register an application, choose the type yourself, it is recommended to choose the third +2. Redirect URL (callback parameter), select Web as the type, and fill in the parameters **https://entra.microsoft.com/TokenAuthorize** ,Otherwise, it will not be able to authorize later. For OneDrive (Operated by 21Vianet), please use: **https://entra.microsoftonline.cn/TokenAuthorize** +3. After filling out, click on the registration below and you can see it after jumping `client ID` + ![onedriveapp](/img/drivers/onedrive_app/onedrive_app1.png) + +Let's get a client secret key, just get it as shown in the figure below, remember `secret key` save in time, it will only appear once +![onedriveapp](/img/drivers/onedrive_app/onedrive_app3.png) + +Once you have obtained the client secret key, go to authorize the **`API`** to be used, and follow the picture below to get it +![onedriveapp](/img/drivers/onedrive_app/onedrive_app4.png) + +After the API is authorized, we go to authorize the organization application + +- Left sidebar ---> Enterprise applications ---> All applications ---> **{choose which application name we started to create}** ---> Permissions ---> Click for `xxxx `Grant admin consent + ![onedriveapp](/img/drivers/onedrive_app/onedrive_app5.png) + +matters needing attention + +- If you start to register the application, the callback parameters are not the same, you may get an error when you click Authorization, remember to write the same +- How to check whether the authorization is successful. If it is not authorized, the page will prompt `The application has not been found to have the authorization of the administrator's permission`. After the authorization is successful, refresh the page and you will see three API permissions, as shown in the figure below + - The above is what it looks like when it is not authorized, and the following is what it looks like after authorization (just refresh the page after authorization) + ![onedriveapp](/img/drivers/onedrive_app/onedrive_app7.png) + +From the beginning to the present, we have obtained the following parameters: tenant ID1, registered application2, client ID/secret key3, authorization API/administrator API4 +Then open the browser and finally authorize it through the following format, which are `tenant ID` `client ID` `Callback URL` Change to your own final login to receive + +```html +https://login.microsoftonline.com/{tenant ID or common}/adminConsent?client_id={client +ID}&redirect_uri={Callback URL} +``` + +For OneDrive (Operated by 21Vianet), please use: + +```html +https://login.partner.microsoftonline.cn/{tenant ID or common}/adminConsent?client_id={client +ID}&redirect_uri={Callback URL} +``` + +![onedriveapp](/img/drivers/onedrive_app/onedrive_app6.png) +If you log in according to the spelled link and find that you are redirected to a blank page, such as the sample code and picture below, the authorization is successful, don’t worry + +``` +https://entra.microsoft.com/TokenAuthorize?admin_consent=True&tenant={Tenant ID}# +``` + +![onedriveapp](/img/drivers/onedrive_app/od_app_ok.png) + +## Fill in the example + +Fill in the values obtained in the above process one by one. If you don’t know which email address it is, you can find it by [**view all users of the organization**](#view-all-users-of-the-organization) +![onedriveapp](/img/drivers/onedrive_app/onedrive_app_Denmo.png) + + + +## View all users of the organization + +If you don't know how many users there are in your OneDrive organization, you can go to the link below to log in to the `admin` account to view +**https://admin.microsoft.com/Adminportal/Home?source=applauncher#/users** +![onedriveapp](/img/drivers/onedrive_app/onedrive_app_user.png) + +## Error message + +- Tips:**Either scp or roles claim need to be present in the token** Error + because you didn't do it in step 4 `grant xxx administrator consent` caused by,Pay attention to the tips below the fourth step +- Tips:**failed get objs: failed to list objs: Unable to retrieve user's mysite URL** Error + The newly created `OneDrive` user account does not take effect in real time, Delay takes effect, wait for a few hours and try again [**Case**](https://github.com/alist-org/docs/discussions/189#discussioncomment-5928892) + +## Add OneDrive_App accounts in batches + +software used:**https://github.com/yzbtdiy/alist_batch** +:::details Fill in the parameters. If you don’t understand, you can watch the video below. +Do not leave blank lines in the two configuration files, as this will also report an error +**config.yaml** + +```yaml +url: ALIST_URL +auth: + username: USERNAME + password: PASSWORD +token: ALIST_TOKEN +aliyun: + enable: false + refresh_token: ALI_YUNPAN_REFRESH_TOKEN +pikpak: + enable: false + username: PIKPAK_EMAIL + password: PIKPAK_PASSWORD +onedrive_app: + enable: true + region: global + tenants: + - id: 1 + client_id: client_id + client_secret: client_secret + tenant_id: tenant_id +``` + +**onedrive_app.yaml** + +```yaml +Category: + mount: 1:user1@xxx.onmicrosoft.com + mount2: 1:user2@xxx.onmicrosoft.com:/folder1 + mount3: 1:user2@xxx.onmicrosoft.com:/folder2 +Category2: + mount11: 1:user1@xxx.onmicrosoft.com + mount22: 1:user2@xxx.onmicrosoft.com:/folder1 + mount33: 1:user2@xxx.onmicrosoft.com:/folder2 +``` + +::: + + + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/openlist.md b/pages/en/guide/drivers/openlist.md new file mode 100644 index 000000000..dcdf10f43 --- /dev/null +++ b/pages/en/guide/drivers/openlist.md @@ -0,0 +1,96 @@ +--- +top: 499 +categories: + - guide + - drivers +--- + +# OpenList + +Support mounting OpenList directories running on other servers + +## Url + +OpenList link you want to mount + +## Username + +username + +When `username&password` is not filled in, `guest` is used for guest access. + +## Password + +password + +## Root folder path + +The default root directory for the path to mount to is`/` + +## Meta password + +The OpenList path you want to mount has a meta information password set. You need to know what the other party's password is set to see the file, otherwise it will be blank after entering + +If multiple folders have different password settings, the password you fill in can only enter the folder with this password, and those without this password cannot enter + +::: danger +If you first use the `username&password` method for mounting, and then switch to using the `metadata password` method for mounting, you need to manually clear the previously automatically filled `token`, otherwise you will still use the `username&password` method for mounting + +::: + +## Token + +You don’t need to write, it will be automatically filled after filling in `Username & Password` and saving + +## Proxy Range + +You need to enable `Web Proxy` or `Webdav Native Proxy` to take effect + +Mount '139Yun' on the OpenList of the server and go to 302. Then, locally mount the OpenList on the server through the OpenList enabled proxy to play videos,achieving the goal of not using server traffic. The CMCC mobile card can also be streamed free + +## Error message + +::: danger +If the OpenList you mounted is "not" enabled Allow Mount, you will not be able to To mount, the following error is prompted + +```json +Failed init storage: the site does not allow mounted +failed get objs: storage not init: the site does not allow mounted +``` + +Solution: Use the allowed `username&password` provided by the other party for mounting + +::: + +::: danger + +If the other party does not enable the guest account access permission, an error will be prompted when mounting (as follows) + +```json +failed get objs: failed to list objs: request failed,code: 400, message: Key: 'LoginReq.Username' Error:Field validation for 'Username' failed on the 'required' tag +``` + +Solution: Use the allowed `username&password` provided by the other party for mounting + +::: + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/pikpak.md b/pages/en/guide/drivers/pikpak.md new file mode 100644 index 000000000..3a40a51e4 --- /dev/null +++ b/pages/en/guide/drivers/pikpak.md @@ -0,0 +1,175 @@ +--- +top: 595 +categories: + - guide + - drivers +--- + +# PikPak / Share + +::: danger + +1. `Pikpak`:Who makes the request, who can use it + - For example, if you build an OpenList on the server with IP `1.1.1.1`, but your own IP is `2.2.2.2`, you cannot play or download it. + - Or enable Proxy policy + +2. `PikPak Share`:There is a size limit. After the specified file size is exceeded, only 40%~50% can be played. + - The specific size of the file is currently unknown + +::: + +## 1. PikPak + +### Username + +email or phone? + +### Password + +password + +### Root folder id + +Can get with https://mypikpak.com/ , default `root`. + +![image.png](/img/drivers/pikpak.png) + +### Platform + +It is not necessary to use it under normal circumstances, but you may need to use it when you cannot log in directly with your account and password. + +- If you choose `android`, it will simulate the Android client for access. If you want to log in using the `Refresh token`, please perform packet-sniffing on the **official Android app**. + +- If you choose `web`, it will simulate the official web version for access. If you want to log in using the `Refresh token`, please use the `Refresh token` obtained from the **web version**. + +### Refresh token + +After filling in the account and password, select `Oauth2` for `Refresh token method` and then save to automatically fill in the refresh token and device information. + +#### Get Refresh Token on Web + +After logging into the official website, open the F12 console and navigate to the page shown in the image below (using Chrome as an example): + +![pikpak-token-1](/img/drivers/pikpak/pikpak-token-1.png) + +Find the option that starts with `credentials`. Select it, and observe the information bar below. From there, you can retrieve the `Refresh token`, as shown in the image below: + +![pikpak-token-2](/img/drivers/pikpak/pikpak-token-2.png) + +### Disable media link + +The OpenList interface uses the playback interface. When the file resolution is too high or the file size is too large, Pikpak will automatically transcode, which may cause synchronization errors in other applications. Enabling this option will prevent using the playback interface to obtain the address. + +According to the official limitations, when the video's bitrate exceeds 40 Mbps or the file size is greater than 50 GB, the system will automatically trigger transcoding. If the video already offers other resolution options, the "original quality" resolution will not be available. + +### Offline Download + +support calling `Pikpak` offline download function in OpenList + +Select `Pikpak` in the lower right corner and select `Pikpak` for offline download options + +- Support: `magnet`, `http`, `ed2k` links +- Also supports: X, TikTok, Facebook, TG URL links + +- Only Pikpak is supported for offline download. If it is not Pikpak, the following error message will be displayed, Although the offline download prompt was successfully added, an error will be prompted in the background. + + unsupported storage driver for offline download, only Pikpak is supported + + ![](/img/drivers/offline_download_error.png) + +## 2. PikPak Share + +::: warning +It is known that PikPak Share can only see 40%-50% +::: + +You only need to fill in **`Username`, `Password`, `Shared ID`** three items, **root folder ID** can be written or not, if not written, the default is the root directory (root directory) + +- Root folder ID: If it is a multi-layer directory, which directory do you want to display as the root directory, you can write which root directory.(Refer to the method of obtaining the root folder ID below) +- Sharing password: if there is a password to share, write it, if not, don’t write it + +![pikpak-share](/img/drivers/pikpak/pikpak-share.png) + +### Get Root Folder ID + +![pikpak-share-rootfolder](/img/drivers/pikpak/pikpak-share-rootfolder.png) + +**The root directory ID of the current pikpak share can no longer be obtained in the address, you need to check the data returned by the interface** + +- Open the F12 console and go to the Network tab +- Refresh the page and search for `detail`, find the last `detail` request +- Select `detail` and find `files` in the Response +- Find the corresponding folder name and check its `id` field content as the root folder ID.(You can confirm the corresponding folder through the `name` field) + +### Use transcoding address + +Not enabled by default. When enabled, the download address will use the **transcoded address**, and you can get the **complete transcoded file** + +- After turning on the `Use transcoding address` option, you cannot use the `OpenList` web version to play the video, but you can **download it normally** or **use a third-party player** + +### Batch add PikPak shared mounts + +software used:**https://github.com/yzbtdiy/alist_batch** + + + +## Precautions + +**Q**: Encountering verification code issues + +**A**: + +- The method has now been adjusted to use `oauth2` for token refresh. +- The username and password are now only used for login to obtain the `Refresh token` and generate the `DeviceID`. +- When encountering the issue `Your operation is too frequent, please try again later`, please try logging in using **third-party authorization** (such as Google login) on the **official web version** or **official Android app**. Afterward, **obtain the `Refresh token` for mounting** — note the selection of the `Platform` at this time. + +--- + +**Q**: Encountering the following situation: `Failed load storage: failed init storage: Your operation is too frequent, please try again later` + +![pikpak-share](/img/drivers/pikpak/pikpak-error-1.png) + +**A**: This means that the access has been too frequent, and the account/IP will be unable to log in for a period of time. Note: After this occurs, **you can log in to the official client normally using third-party authorization**. Also, **using this IP to request any account may trigger the issue again**, so you could try logging in using the `Refresh token` method (though it is not guaranteed to be effective). + +--- + +**Q**: Encountering the `Click Here` prompt + +**A**: Please click on it, then open F12 or launch a packet-sniffing tool. Complete the CAPTCHA as shown in the image below, obtain the `captcha_token`, and enter it into the driver's `Captcha token` field. After saving, the driver should work normally — **this applies to cases where login is done with username and password**. + +![pikpak-share](/img/drivers/pikpak/pikpak-error-2.png) + +--- + +--- + +**Q**: Prompt when adding storage: **Failed init storage: invalid_account_or_password** What should I do, the password I entered is correct + +**A**: If the account password is not filled in incorrectly, it may be that you used Google, FB and other third-party quick registration when you registered. Although it seems that the account is a Google mailbox, you cannot log in with the mailbox, but you must use the first Three-party verification, **OpenList** does not support this kind of jumping to third-party verification, **so you need to bind an email address in the account settings and set a login password**, or register a new account + +--- + +**Q**: Prompt when adding mount: **failed get objs: failed to list objs: Sorry, sharing is not available in the current region** + +**A**: Because access to PikPak is prohibited in China, just use a proxy for `OpenList`, how to make `OpenList` use a proxy [**One of the reference solutions, this method is limited to Windows build**](https://anwen-anyi.github.io/index/07-wenti.html#_41-alist%E5%A6%82%E4%BD%95-%E4%BD%BF%E7%94%A8-%E5%90%83%E5%88%B0-%E4%BB%A3%E7%90%86-proxy) + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/quark.md b/pages/en/guide/drivers/quark.md new file mode 100644 index 000000000..33d5dae9c --- /dev/null +++ b/pages/en/guide/drivers/quark.md @@ -0,0 +1,140 @@ +--- +top: 684 +categories: + - guide + - drivers +--- + +# Quark / TV / Open + + + +https://pan.quark.cn + +:::danger +Due to Quark Cloud Drive's speed limit issues, it can now only use local proxy for transfers. [See details here](https://github.com/alist-org/alist/issues/4318#issuecomment-1536214188) +::: + +## 1. Quark + +### Cookie + +Press F12 to open "Developer Tools", select "Network", choose any request on the left, and find the one with the `Cookie` parameter. + +![quark](/img/drivers/quark/quark_cookie.png) + +### Root Folder ID + +The root directory ID is `0`. + +- For subfolder IDs, enter the folder and get the directory ID from the top address bar. The deeper the subdirectory, the further back the directory ID is in the address bar. To mount a specific subdirectory, just use its directory ID. + +![url](/img/drivers/quark/quark_fileid.png) + +Note: Please use Chrome browser to obtain Cookies. If you use Firefox, you may remain as a guest and be prompted to log in. + +### [Online Preview/Download] is Slow? + +Quark Cloud Drive downloads are slow because a **membership** is required, and mounting can only (forcibly) use the `local proxy` method, which means your OpenList server needs to have high bandwidth. + +- What is `local proxy`? + - `Local proxy` means your OpenList server acts as a relay: it first downloads to your OpenList server, then forwards to you. If your server's speed is not fast enough, the forwarding speed to you will also be slow. + +1. Use a server with higher bandwidth as a relay +2. Set up at home on your own computer +3. Or simply give up using it. + +### Default Download Method + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[Local Proxy]:::someclass==Forced===>a2[User Device] + classDef someclass fill:#f96 + click c1 "../drivers/common.html#webdav-policy" +``` + +Note: [**alist/issues/4318**](https://github.com/alist-org/alist/issues/4318#issuecomment-1536214188) + +## 2. Quark TV + +The TV version supports `302`, but only the `access` and `download` operations are supported; other operations are not supported (not available in the API). + +### How to Add + +1. Select the `QuarkTV` driver, fill in the mount path, and save. +2. Return to the drivers page, use the mobile app to scan the QR code (if the QR code is not displayed, click `Table Layout` in the upper right corner of the driver to switch from list mode to table mode). +3. After scanning and confirming, disable the driver, then enable the driver again to use it. + - `Refresh token`, `Device id`, and `Query token` will be filled in automatically, no manual input required. + - Please do not edit or modify them manually. + +![](/img/drivers/tv_qrcode.png) + +### Root Folder ID + +The root directory ID is `0`. + +- For subfolder IDs, enter the folder and get the directory ID from the top address bar. The deeper the subdirectory, the further back the directory ID is in the address bar. To mount a specific subdirectory, just use its directory ID. + +![url](/img/drivers/quark/quark_fileid.png) + +### Default Download Method + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|Default|a2[User Device] + classDef someclass fill:#f96 + c1[Local Proxy]-.Alternative.->a2[User Device] + b1[Proxy URL]-.Alternative.->a2[User Device] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 3. Quark Open + +:::danger +This "Open" is not an open interface in the true sense. + +No further tutorials are provided. +::: + +### Usage + +- Select `Quark Cloud Drive (OAuth2) Authentication Login` at [here](https://api.oplist.org). +- Fill in the AppID and SignKey you obtained to get the refresh token. +- Due to the lack of relevant documentation, please use the **online API** for refreshing. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/s3.md b/pages/en/guide/drivers/s3.md new file mode 100644 index 000000000..852e1442a --- /dev/null +++ b/pages/en/guide/drivers/s3.md @@ -0,0 +1,354 @@ +--- +top: 895 +categories: + - guide + - drivers +--- + +# S3 + +S3 object storage protocols, such as COS, OSS, B2, etc. + +## Bucket + +bucket name + +## Endpoint + +Endpoint address (If you don’t know, you can read the official documents below) + +## Region + +area (If you don’t know, you can read the official documents below) + +## Access key id + +Access key id + +## Secret access key + +Secret access key + +## Session token + +Session token,Normally, the three-segment token needs to be used, and the two-segment token does not need to fill in this option. + +## Custom host + +Custom cdn acceleration domain name + +## Enable custom host presign + +When "Custom host" is left blank, this option does not function. + +When disabled: The returned URL does not include signature, which is suitable for cases where the custom host is a CDN. + +When enabled: The returned URL includes signature, which is suitable for cases where the host and OSS are from the same data center, the Endpoint is an internal access domain within the data center, and the custom host is an external access domain. + +## Sign url expire + +The validity period of the signed download address is 4 hours by default. If a custom accelerated domain name is used, this option is invalid. + +## Placeholder + +The name of placeholder file. + +## Force path style + +If enable ForcePathStyle, usually needed for minio. + +## List object version + +Refer to s3 sdk documents. + +## Remove bucket + +Remove bucket name from path when using custom host. + +## Add filename to disposition + +Add filename to Content-Disposition header. + +## Enable direct upload + +Enable direct front-end upload to S3. + +## Direct upload host + +The host for direct upload. + +## User agent + +Custom User-Agent for S3 requests. Leave empty to use the default generated by AWS SDK for Go (e.g. `aws-sdk-go/1.55.8 (go1.xx.x; ; )`). + +## Add object storage examples and official documents + +`S3 Object Storage` includes but is not limited to these. There are many more, such as Microsoft and Google, which have not been added. There are no examples here because it is difficult to register and activate and cannot provide example tutorials. If you have an account, please contact us and provide an account and I will add it. + +### Alibaba + +[**Alibaba Cloud OSS Official Website**](https://oss.console.aliyun.com/) + +[**Comparison Table of Endpoints in Different Regions of OSS under Public Cloud**](https://help.aliyun.com/document_detail/31837.html) + +::: tip +OpenList needs to skip `referer` to mount **Alibaba Cloud Disk**. If you do not allow `referer` to be empty in anti-leech, it will not work, because Alibaba Cloud Disk uses Alibaba Cloud OSS (in Beijing area) + +In fact, the `Region|Region` option can be written as `Endpoint`, but for the sake of specification, let’s write it according to the comparison table.::: +::: + +Fill in example: + +![s3](/img/drivers/s3/al.png) + +### Tencent + +[**Tencent Cloud COS Official Website**](https://console.cloud.tencent.com/cos) + +[**Collection Table of Endpoints in Each Region of COS under the Public Cloud**](https://cloud.tencent.com/document/product/436/6224) + +Fill in example: + +![s3](/img/drivers/s3/tx.png) + +### VolcEngine + +[**VolcEngine TOS Official Website**](https://console.volcengine.com/tos) + +[**Collection Table of Endpoints in Each Region of COS under the Public Cloud**](https://www.volcengine.com/docs/6349/107356) + +Fill in example: + +![s3](/img/drivers/s3/zijie_tos.png) + +### Tianyi + +[**Tianyi Cloud OOS Official Website**](https://oos-cn.ctyun.cn/oos/ctyun/consoleBucket.html) + +[**Comparison Table of Endpoints of OOS Regions under Public Cloud**](https://www.ctyun.cn/document/10026693/10027878) + +Fill in example: + +![s3](/img/drivers/s3/ty.png) + +### Baidu + +[**Baidu Cloud BOS official website**](https://console.bce.baidu.com/bos) + +[**Self-configure Bucket’s Referer whitelist permissions**](https://cloud.baidu.com/doc/BOS/s/Bk6kqu8eq#Setrefererwhitelist) + +[**comparison table of Region and Endpoint of BOS under public cloud**](https://intl.cloud.baidu.com/doc/BOS/s/xjwvyq9l4-en) + +**Endpoint comparison table compatible with AWS S3**[**Click to view details**](https://cloud.baidu.com/doc/BOS/s/xjwvyq9l4) + +Fill in example: + +![s3](/img/drivers/s3/bd.png) + +### Qiniu + +[**Qiniu Cloud Kodo official website**](https://portal.qiniu.com/kodo/bucket) + +[**Region and Endpoint comparison table of Kodo under the public cloud**](https://developer.qiniu.com/kodo/4088/s3-access-domainname) + +Fill in example: + +![s3](/img/drivers/s3/qn.png) + +### Huawei + +[**HUAWEI CLOUD OBS Official Website**](https://www.huaweicloud.com/intl/en-us/) + +[**Check the Region and Endpoint comparison table of OBS under the public cloud**](https://developer.huaweicloud.com/intl/en-us/endpoint), the secret key AK/SK[acquire](https://console.huaweicloud.com/iam/?region=cn-north-4#/mine/accessKey) to set by yourself. + +Fill in example: + +![s3](/img/drivers/s3/hw.png) + +### JD + +[**JD CLOUD OBS Official Website**](https://www.jdcloud.com/cn/products/object-storage-service) + +Advantages: [free 10G](https://www.jdcloud.com/cn/activity/oss), support IPV6 + +Disadvantages: need to precharge 100 to open,However, after the pre-deposit is completed, the object storage is activated, and the pre-deposited amount can still be used after withdrawal. + +[**Region and Endpoint comparison table of Kodo under the public cloud**](https://docs.jdcloud.com/cn/object-storage-service/oss-endpont-list) + +Fill in example: + +![s3](/img/drivers/s3/jd.png) + +### Doge + +[**Doge Cloud OSS Official Website**](https://www.dogecloud.com/product/oss),If you want to add Doge, use a separate `DogeCloud` driver to add, please do not use the `S3` the driver + +Region Code: + +```json +// 0: 上海(华东),1: 北京(华北),2: 广州(华南),3: 成都(西南) +``` + +![s3](/img/drivers/s3/doge.png) + +### 139 + +[**139 CLOUD EOS Official Website**](https://console.ecloud.10086.cn/api/page/eos-console-web/CIDC-RP-00/eos/bucket?productType=os&v=new) + +[**Region and Endpoint comparison table of Kodo under the public cloud**](https://ecloud.10086.cn/op-help-center/doc/article/48082) + +Fill in example: + +![s3](/img/drivers/s3/139.png) + +### upyun + +[**Yupyun USS Official Website**](https://console.upyun.com/services/file/):[Click here to configure USS](./uss.md) + +### Amazon + +[**Amazon S3 Official Website**](https://s3.console.aws.amazon.com/s3/) + +The endpoint URL contains the name of the region where the bucket is located + +Example 1, if your bucket is in the US East (Ohio) region + +Then the format of the Endpoint URL is **`s3.us-east-2.amazonaws.com/your-bucket-name`** + +Region parameter: **`us-east-2`** + +Bucket name: **`your-bucket-name`** (your own bucket name) + +Example 2, my storage bucket is `Tokyo`, and the name of the storage bucket is `cdn-aw`, then the complete parameter filling is as follows + +1. **Endpoint**: s3.ap-northeast-1.amazonaws.com +2. **Region**: ap-northeast-1 +3. **Bucket Name**: cdn-aw + +Let's take a look at the complete parameter filling example + +Fill in example: + +![s3](/img/drivers/s3/aws.png) + +### Cloudflare R2 + +[**Cloudflare R2 Official Website**](https://dash.cloudflare.com/) + +**Endpoint**: The S3 endpoint is available via the **`https://.r2.cloudflarestorage.com`** endpoint. + +**`ACCOUNT_ID`**: This account ID can be seen everywhere, and the simplest is the position at the top of the browser. The string of IDs behind the website link is your account ID. + +填写示例: + +![s3](/img/drivers/s3/cfr2.png) + +### Backblaze B2 + +[**B2 Official Website**](https://secure.backblaze.com/b2_buckets.htm) + +The full name is **`Backblaze`**, no verification information is required, and it is said that the first 10G is free. + +Fill in example: + +![s3](/img/drivers/s3/b2.png) + +### Scaleway + +[**Scaleway Official Website**](https://console.scaleway.com/object-storage/buckets) + +Card required, verification information + +[**Region and Endpoint comparison table of Kodo under the public cloud**](https://www.scaleway.com/en/docs/storage/object/concepts/#object-storage) + +Fill in example: + +![s3](/img/drivers/s3/sc.png) + +### Minio + +Those who use `Minio` self-built object storage should know how to obtain the ID and secret key, so I won’t talk about it here. + +You only need to fill in the four options of bucket name, access key ID, security access key, and Endpoint, and leave the region empty. + +**Endpoint:`http(s)://your-server:9001/`**, You can use IP or domain name + +Finally, remember to enable **`Force Path Style`** to use, otherwise it cannot be used [Click to view details](https://github.com/alist-org/alist/discussions/4920#discussioncomment-6635452) + +Fill in example: + +![s3](/img/drivers/s3/mini-io.png) + +### Google Cloud + +I assume you already have a Google Cloud account, so let’s go directly to the Google Cloud Storage settings page: + +Then you will enter the following page: + +![Google Cloud](/img/drivers/s3/googleStorage1.png) + +You can see Endpoint and Access Key on this page, you need to select an account and create an Access Key + +![Google Cloud](/img/drivers/s3/googleStorage2.png) + +Access Key/Secret will be displayed on the new page, They will only be displayed this time. Please save them properly. + +![Google Cloud](/img/drivers/s3/googleStorage3.png) + +At this point, the EndPoint, access key Id, and security access key required by OpenList have been obtained. Please view the remaining buckets and regions in the corresponding buckets. + +![Google Cloud](/img/drivers/s3/googleStorage4.png) + +### Intranet access + +The server and object storage need to be in the same region, currently only supports `Alibaba Cloud OSS`. For example, if you are `Alibaba Cloud Hangzhou Server` and `Alibaba OSS in Hangzhou`, you can use it. + +- Previous behavior (taking Aliyun OSS as an example): + - Download link without custom host (using internal endpoint): + + ``` + https://.oss-cn-beijing-internal.aliyuncs.com/?X-Amz-Algorithm=***&X-Amz-Credential=***... + ``` + + - Download link within custom host (using external endpoint, the following link is **invalid**): + + ``` + https://.oss-cn-beijing.aliyuncs.com/ + ``` + +- Current behavior when `EnableCustomHostPresign` is `false`: Same as previous + +- Current behavior when `EnableCustomHostPresign` is `true` : + - Download link within custom host (using external endpoint, the following link is **valid**): + + ``` + https://.oss-cn-beijing.aliyuncs.com/?X-Amz-Algorithm=***&X-Amz-Credential=***... + ``` + +### How to solve the problem of files with the same name as the folder + +After mounting the object storage and opening the folder, there is always a file with the same name as the folder that cannot be deleted or opened + +- The solution is to change the `List object version` in the configuration to V2, as shown in the following figure + ![](/img/drivers/s3/v1andv2.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/seafile.md b/pages/en/guide/drivers/seafile.md new file mode 100644 index 000000000..d9aa0d195 --- /dev/null +++ b/pages/en/guide/drivers/seafile.md @@ -0,0 +1,74 @@ +--- +top: 495 +categories: + - guide + - drivers +--- + +# Seafile + +Seafile uses libraries as a unit for version control and synchronization. + +Use this driver to mount a Seafile repository to OpenList, encrypted libraries are supported. + +## Root folder path + +1. If the `RepoId` is not set, this configuration specifies the name and sub-path of the library to access, defaulting to the root path: `/`, indicating displaying all libraries. Suppose you have a library named `example`, if you only want to mount the contents of this library, you can set this item to `/example`; if you only want to display a directory named `abc` within this library, you can set this item to `/example/abc`, and so on. +2. If the `RepoId` is set, this configuration is used to set the directory within the specified library, defaulting to the root directory: `/`. + +## Address + +Your Seafile server address, e.g. + +- `https://seacloud.cc` +- `http://192.168.1.24:8000` + +## Username + +The email or username used to log in to your Seafile server. + +## Password + +The password for your email or username. + +## Token + +Applicable to certain Seafile cloud storage systems integrated with enterprise/school unified authentication. For such cloud storage systems, there is no need to enter the username and password; only the token information needs to be filled in. Taking Tsinghua Cloud Disk as an example, the following steps can be used to obtain the token: `cookie` → `seahub_auth` → `"student ID@tsinghua.edu.cn@random string"`, where the random string is the token. + +## RepoId (Library ID) + +Seafile library ID, such as: `86fc6316-3f2a-4c64-b6f5-d4d2b341bc9a` + +- Open the Seafile library in a browser, and you can see the library ID in the address bar. +- Can be left empty, if left empty, all visible libraries for the specified account will be displayed. + +## RepoPwd (Library Password) + +Set the password used to access encrypted libraries, leave blank if no encrypted libraries are present. + +After configuring this item, OpenList will use this password to access all encrypted libraries for the specified account, without affecting access to unencrypted libraries. If you have multiple encrypted libraries using the same password, they can all be accessed normally. + +Different passwords for different libraries within a storage are not currently supported. If you have such a requirement, you can add multiple Seafile storage to achieve this, allowing you to specify different passwords for accessing different encrypted libraries. + +If you still cannot access files within encrypted libraries after setting the library password, please check if the password and other configurations above are correct. It is recommended to restart OpenList after modifying this password configuration to avoid caching issues. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/sftp.md b/pages/en/guide/drivers/sftp.md new file mode 100644 index 000000000..cd883b40d --- /dev/null +++ b/pages/en/guide/drivers/sftp.md @@ -0,0 +1,49 @@ +--- +top: 897 +categories: + - guide + - drivers +--- + +# SFTP + +即通过 SSH 协议访问文件。 + +## 地址 + +SSH 地址,需要包含端口。 + +## 用户名 + +SSH 用户名 + +## 密码 + +SSH 密码 + +## 私钥 + +使用私钥验证而不是密码。 + +## 根文件夹ID + +挂载的根路径 + +## 默认使用的下载方式 + +```mermaid +--- +title: 默认使用的哪种下载方式? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[本机代理]:::someclass==默认===>a2[用户设备] + classDef someclass fill:#f96 + b1[代理URL]-.备选.->a2[用户设备] + click b1 "../drivers/common.html#webdav-策略" + click c1 "../drivers/common.html#webdav-策略" +``` diff --git a/pages/en/guide/drivers/smb.md b/pages/en/guide/drivers/smb.md new file mode 100644 index 000000000..5389d81d4 --- /dev/null +++ b/pages/en/guide/drivers/smb.md @@ -0,0 +1,32 @@ +--- +top: 896 +categories: + - guide + - drivers +--- + +# SMB + +It is to mount smb to OpenList, instead of sharing OpenList through smb, instead of sharing smb like webdav. +Because there are no related equipment and services, it is impossible to test. You can test it yourself according to the method provided in the **`GitHub Discussions`** discussion +**https://github.com/alist-org/alist/discussions/2215#discussioncomment-4104018** +Wait for the more detailed tutorials to be supplemented after the relevant equipment or services are available, or wait for the PR of the boss who has no problem with the test. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/storage-list.md b/pages/en/guide/drivers/storage-list.md new file mode 100644 index 000000000..e69de29bb diff --git a/pages/en/guide/drivers/strm.md b/pages/en/guide/drivers/strm.md new file mode 100644 index 000000000..3ba57d36c --- /dev/null +++ b/pages/en/guide/drivers/strm.md @@ -0,0 +1,10 @@ +--- +top: 990 +categories: + - guide + - drivers +--- + +# Strm + + diff --git a/pages/en/guide/drivers/strm_text.md b/pages/en/guide/drivers/strm_text.md new file mode 100644 index 000000000..f1285d45a --- /dev/null +++ b/pages/en/guide/drivers/strm_text.md @@ -0,0 +1,100 @@ +**Strm** is a driver that allows you to convert supported files from multiple cloud drives into .strm files. + +::: warning Important Notes +Please understand the function of strm files before use + +Only the **`Download Preview (Read-Only)`** operation is supported. Other operations such as **Copy, Delete, Rename, +Offline Download, Upload** are **not supported**. + +Strm uses a local proxy method, and during the **`Download Preview (Read-Only)`** operation, it will consume traffic +from the deployed machine (depending on the number of files; individual files typically consume less than 100KB). +::: + +### Parameter Descriptions + +#### Path + +The full path in OpenList where .strm files should be generated. +Examples: + +``` +/115_open/Video +/kuake_open/Video +``` + +#### Site URL + +The URL prefix for the generated .strm files. + +For example, if the directory /115_open/Video contains the file:`/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.mkv` + +And you enter http://localhost:5244 as the Site URL, the generated .strm file will point to: +`http://localhost:5244/d/115_open/Video/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.strm` + +This field is optional. If left blank, the current access URL will be used as the default prefix. + +#### File Type Filter + +Specify which file types should be included for .strm generation. + +The following types are built-in: + +``` +Video Type +mp4,mkv,flv,avi,wmv,ts,rmv,web + +Audio Type +mp3,flac,aac,wav,ogg,m4a,wma,alac +``` + +You can add more types as needed. Use **commas (,)** to separate multiple file types. + +#### Encode Path + +Whether to enable URL path encoding. + +If disabled, the .strm URL will be: + +``` +http://localhost:5244/d/Video/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.strm +``` + +If enabled, it will be: + +``` +http://localhost:5244/d/Video/%E7%94%B5%E5%BD%B1/%E5%86%8D%E8%A7%81%EF%BC%8C%E6%9D%8E%E5%8F%AF%E4%B9%90%20(2023)/%E5%86%8D%E8%A7%81%EF%BC%8C%E6%9D%8E%E5%8F%AF%E4%B9%90%20(2023)%20-%202160p.mkv +``` + +#### Without Url + +The generated strm file will not contain URL prefixes + +#### SaveStrmToLocal + +When enabled, accessing a directory within or mounted by the Strm driver will save the Strm files locally + +#### SaveStrmLocalPath + +The local directory path where Strm files are stored. + +#### KeepLocalDownloadFile + +::: warning +Warning: Deprecated parameter, removed in version 4.1.9 and will be removed in future versions +::: + +#### Local Save Mode + +- `Insert Mode`: Only generate files that do not exist locally; existing local files will not be modified. +- `Update Mode`: Generate files that do not exist locally and update the content of existing local files to the latest + version. +- `Sync Mode`: Based on Update Mode, additionally delete local files that no longer exist on the cloud drive. + +> if you need scraper software to read local strm files and generate metadata files, please choose `Update Mode` to ensure that the content of local strm files is up to date and metadata files are not deleted + +### Actively generate local files + +The local file generation feature only takes effect when users access the corresponding directory. To recursively generate files for all paths, you can use the **Manually Scan** function, located under `Manage Page / Indices / Manually Scan`. In the **Path to scan** field, enter the **mount path of the Strm driver**, and in the **Rate limit** field, specify the API rate limit for the scanning process. Click start and wait for completion. + +Enabling the [Global Settings / Handle hook after writing](/en/configuration/global#handle-hook-after-writing) allows local files to be automatically generated after performing upload, rename, delete, move, copy, or extraction operations either under the original driver. +Scheduled or automatic local file generation by listening for driver changes is not currently supported. diff --git a/pages/en/guide/drivers/teambition.md b/pages/en/guide/drivers/teambition.md new file mode 100644 index 000000000..ecdbcc5c9 --- /dev/null +++ b/pages/en/guide/drivers/teambition.md @@ -0,0 +1,43 @@ +--- +top: 681 +categories: + - guide + - drivers +--- + +# Teambition + + + +Teambition project file, supports Chinese server and international server. + +## Cookies + +After login, the validity period is unknown +![cookie](/img/drivers/teambition-cookie.png) + +## Project ID and root folder id + +Get it from the URL after logging in +![id](/img/drivers/teambition-id.png) + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/teldrive.md b/pages/en/guide/drivers/teldrive.md new file mode 100644 index 000000000..eca0f3395 --- /dev/null +++ b/pages/en/guide/drivers/teldrive.md @@ -0,0 +1,82 @@ +--- +top: 586 +categories: + - guide + - drivers +--- + +# Teldrive + + + +Teldrive is a Telegram-based cloud storage app maintained by a third-party open-source project: [tgdrive/teldrive](https://github.com/tgdrive/teldrive). + +**Highlights** + +- Unlimited storage +- No file size limit +- If you don’t subscribe to Telegram Premium, bandwidth is limited, and speed depends on the distance and bandwidth quality between your account’s data center (DC1–DC5) and the Teldrive server. + +Backend deployment requires a **Telegram API (not Bot API)**. See the official guide: [Teldrive installation](https://teldrive-docs.pages.dev/docs/getting-started/prerequisites) + +## Address + +Enter the base URL of your Teldrive backend **without** a trailing slash. + +Example: `https://teldrive.example.com` + +## Authentication (Cookie) + +Only **Cookie** authentication is supported. + +After logging in to the Teldrive web UI, grab the cookie from your browser. + +The cookie should **start with** `access_token=` and is a JWT. + +![Fetch teldrive cookie](/img/drivers/teldrive/fetch_teldrive_cookie.png) + +::: tip +You only need the string **containing** `access_token=`. +::: + +## Download methods + +**Note**: If `Use Share Link` is enabled, a shared file link is created and the download URL is valid for **1 hour**. + +Otherwise, you need to enable OpenList's `Web Proxy`. + +## Chunk size + +Upload chunk size in **MiB**. + +Default: `10` (10 MiB). If large uploads fail, try a smaller value. + +If the chunk size is larger than the file size, the file is uploaded in a single thread without chunking. + +## Upload Concurrency + +Concurrent upload threads. Default: `4`. + +Adjust based on available memory. A handy estimate is: +`memory ≈ chunk_size × concurrency` + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/terabox.md b/pages/en/guide/drivers/terabox.md new file mode 100644 index 000000000..039059fbc --- /dev/null +++ b/pages/en/guide/drivers/terabox.md @@ -0,0 +1,67 @@ +--- +top: 594 +categories: + - guide + - drivers +--- + +# Terabox + + + +https://www.terabox.com/ + +## Refresh Token + +~~[**Click here to log in**](https://www.terabox.com/) to obtain a refresh token~~ +Since F12 is currently disabled on the web page, we can first open F12 on another page and turn on the `Network` → `Preserve log` option. +f12
+Then enter https://www.terabox.com in the address bar of this tab to enter the webpage, you can see the reserved request, and then find **`Cookie`** from the reserved request. +![terabox](/img/drivers/terabox/terabox_cookie.png) +
+ +## Download Api + +**Download api needs to choose `Crack`** ,before you can use +
+ +## Root folder id + +- Root folder id is `/` +- Mount other single subdirectories, put a picture for reference + - Demo + +## Download error using Proxy Url link + +Using [**Proxy Url**](common.md#download-proxy-url) to download error + +```json +{ + "error_code": 31211, + "error_msg": "access denied" +} +``` + +View specific reasons:**[https://github.com/alist-org/alist/issues/6508](https://github.com/alist-org/alist/issues/6508#issuecomment-2171762705)** +
+ +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/thunder.md b/pages/en/guide/drivers/thunder.md new file mode 100644 index 000000000..7a355b96b --- /dev/null +++ b/pages/en/guide/drivers/thunder.md @@ -0,0 +1,496 @@ +--- +top: 685 +categories: + - guide + - drivers +--- + +# Thunder Cloud Disk + + + +::: tip +Please use `Thunder` directly instead of `ThunderExpert` if you are not good at it. + +`ThunderExpert` mainly provides more free settings and realizes more login methods + +--- + +`Thunder X` serves overseas users. As of the time of document release, only the Android version is available. Other versions have not yet been released. + +- Thunder X has sufficient speed even without membership. Future changes are unknown. +- Using the APP may require a proxy, but not when mounted on OpenList. + +--- + +`Thunder Browser`:Currently only supports mobile phones (Android, iOS) + +- **https://x.xunlei.com/** +- If you log in to OpenList, the mobile phone will be kicked offline. On the contrary, if you log in to OpenList first and then log in to the mobile phone, OpenList will be kicked offline but there will be no prompt + +::: + +## 1. Thunder + +### Username + +That is, the mobile phone number, email, and username used for login (there is a probability that you cannot log in, you need to try) + +1. Before obtaining the verification code, do not include the `+86` country code when entering the phone number. +2. After obtaining the verification code, the phone number must include the `+86` country code, for example: +8613722223333. + +### Password + +password for login + +
+ +### Credit Key + +After saving for the first time, an error will occur. This is normal. Please go to the storage management interface. + +![Storage Error Interface](/img/drivers/xunlei/storage_error.png) + +Copy all the content above. + +[Click here to enter](https://i.xunlei.com/xlcaptcha/android.html) + +After entering, open the developer tools (usually by pressing F12), go to the console, and enter `reviewCb()`. **Paste all the previously copied content, including the curly braces,** inside the parentheses, as shown below: + +![Console Input](/img/drivers/xunlei/input.png) + +Move the cursor to **after the parentheses** and press Enter. + +![SMS Verification](/img/drivers/xunlei/SMS.png) + +The webpage will automatically redirect to the mobile number verification page. + +After completing the verification, the page **will not** refresh automatically. Please check the console. The `creditkey` shown below is the required key. +![Console Output](/img/drivers/xunlei/output.png) + +Copy it to the storage management, and you can mount it normally. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 2. ThunderExpert + +::: tip +If Xunlei needs to download, you must specify UserAgent (same as DownUserAgent below). Or use the proxy function in this program to transfer. +::: + +### Login Type + +1. When selecting User, you only need to fill in the username and password + - User You need to bring the `+86` area code, for example +8613722223333 fill in like this +2. **RefreshToken**: Just fill in RefreshToken when selecting RefreshToken + +### Signature Type + +**Algorithms**:When selecting Algorithms, just fill in the Algorithms (it is difficult to obtain, and needs to be reversed) + +**Captcha sign**: Only fill in CaptchaSign and Timestamp when selecting CaptchaSign + +``` +//signature algorithm +str = ClientID + ClientVersion + PackageName + DeviceID + Timestamp +for (Algorithm in Algorithms) { + str = md5(str + Algorithm) +} +CaptchaSign = "1." + str +``` + +Login type and Sign type recommended options + +![xunlei](/img/drivers/xunlei/x3.png) + +### DeviceID + +The value calculated by md5 is used to determine the logged in device + +### ClientID, ClientSecret, ClientVersion, PackageName + +It is related to the signature, fill in according to the actual situation + +### UserAgent + +UserAgent used by API requests, may be inaccessible or speed limited if the setting is incorrect + +### DownUserAgent + +The User Agent used for downloading, if the setting is wrong, it will not be downloaded (it will be used when the agent is turned on) Fixed parameters: + +`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36` + +### Key data acquisition process + +Obtain Xunlei request data through network analysis tools (packet capture tools) --- Please read the following information carefully + +Open Xunlei and log in to your account (the picture below uses the PC client to operate, and the web terminal is also available) + +Included in the request https://xluser-ssl.xunlei.com/v1/shield/captcha/init + +CaptchaSign, Timestamp, DeviceID, ClientID, ClientVersion, PackageName, User-Agent + +**Note**: After obtaining the information of the two pictures, slowly select the data from the two data captured and fill in it + +After logging in to Thunder and opening the packet capture tool, it is possible that the information of **v1/shield/captcha/init** cannot be immediately obtained at this time. + +Do not close the Xunlei PC client and the packet capture tool, just wait, it will refresh automatically after **5 minutes** + +You will see the parameters as shown in the figure below, and you can fill in it according to the acquisition (if you can't see it clearly, you can right-click to copy the picture and link it to the browser to open a new window) + +See **v1/shield/captcha/init** and grab it, please **immediately don't delay for a second** Right-click Xunlei in the taskbar in the lower right corner to exit, exit completely and then reopen to get \* \*Figure II\*\* + +![xunlei](/img/drivers/xunlei/x4.png) + +Restarting Thunder will refresh the token + +The request to https://xluser-ssl.xunlei.com/v1/auth/token contains RefreshToken (please use the returned value), ClientSecret (does not exist on the web side) +![xunlei](/img/drivers/xunlei/x5.png) + +Figure 1 contains **7** parameters | Figure 1 contains **2** parameters | a fixed parameter (Down UserAgent), ten parameters and 3 options and a mount path, just write and save, before saving Remember to check~ + +When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). + +The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. + +### ThunderExpert complete parameter filling demo: + +![xunlei](/img/drivers/xunlei/x6.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +::: danger +At present, the official will ban frequent API calls. Please use with caution and bear the consequences. +::: + +### username、password + +The mailbox and password used for login +
+ +### CaptchaToken + +It will be filled automatically without filling in manually +
+ +### Root folder id + +The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it + +- In the request in the package`https://api-pan.xunleix.com/drive/v1/files?parent_id=&page_token=&filters=`,you can get the following parameters + - `文件夹ID(id)` + - `文件夹名称(name)` + - `父文件夹ID(parent_id)` +- The `folder ID` obtained in the root directory (for example : `我接收的文件`、`我的云盘`、`高速云下载`), **This will change with different account numbers, there is no the same value, In the request in the package** + ![xunlei](/img/drivers/xunlei/xlx_name.jpg) + +### Use video url + +When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). + +The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 4. Thunder X Expert + +::: danger +At present, the official will ban frequent API calls. Please use with caution and bear the consequences. +::: + +### username、password + +The mailbox and password used for login +
+ +### CaptchaToken + +It will be filled automatically without filling in manually +
+ +### Root folder id + +The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it + +- In the request in the package`https://api-pan.xunleix.com/drive/v1/files?parent_id=&page_token=&filters=`,you can get the following parameters + - `文件夹ID(id)` + - `文件夹名称(name)` + - `父文件夹ID(parent_id)` +- The `folder ID` obtained in the root directory (for example : `我接收的文件`、`我的云盘`、`高速云下载`), **This will change with different account numbers, there is no the same value, In the request in the package** + ![xunlei](/img/drivers/xunlei/xlx_name.jpg) + +### Login Type + +- `User`: Only fill in the username and password +- `Refresh token`: Just fill in`RefreshToken` + +### Sign Type + +- `Algorithms`:Choose `Algorithms` Just fill in `Algorithms` (It has been automatically filled. You do n’t have to fill in it yourself. You only need to fill in the username and password.When the algorithm is not invalidated) +- `Captcha sign`: Choose `Captcha sign` Just fill in `Captcha sign` and `Timestamp` + +### Part of parameter Packet Capture description + +- `Captcha token`:No need to fill in +- `Device id`:The value calculated by MD5 is used to judge the login device +- `Client id`, `Client secret`, `Client version`, `Package name`:It related to the signature, fill in according to the actual situation + +--- + +- `User agent`:API requests `User agent`, setting errors may not access or speed limit +- `Download user agent`:When downloading, use the `user agent `,If the setting error cannot be downloaded (the agent will use it) + - `User agent` and `Download user agent`:You can fill it out by yourself. If you don’t know how to fill it out, you can leave it blank and it will be automatically filled in. + +--- + +In the request in the package `https://xluser-ssl.xunleix.com/v1/shield/captcha/init`,you can get the following parameters6 + +- `Client id`、`Device id`、`Captcha sign` +- `Package name`、`Client version`、`Timestamp` + + ![xunlei](/img/drivers/xunlei/xlx_z1.jpg) + + In the request in the package `https://xluser-ssl.xunleix.com/v1/auth/signin`, you can get the following parameters2 + - `Client id`、`Client secret` + ![xunlei](/img/drivers/xunlei/xlx_z2.jpg) + +### Use video url + +When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). + +The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 5. Thunder Browser + +### username、password + +Mobile phone number, email, username, and password used to log in + +- When filling in the mobile phone number, please bring the `+86` area code, for example `+8613822334455` + +### CaptchaToken + +It will be filled automatically without filling in manually + +### Root folder id + +The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it + +- In the request in the package`https://x-api-pan.xunlei.com/drive/v1/files?parent_id&page_token&space=`,you can get the following parameters + - `文件夹ID(id)` + - `文件夹名称(name)` + - `父文件夹ID(parent_id)` +- The `folder ID` obtained in the root directory (for example : `来自分享`、`超级保险箱`), **This will change with different account numbers, there is no the same value, In the request in the package** + ![xunlei](/img/drivers/xunlei/x_br_foled.png) + +### Safe password + +Thunder Browser Safe password + +- Files in Safe password can only be deleted directly and cannot be deleted to the recycle bin, so the [**Deletion method**](#remove-way) below has nothing to do with this configuration. + +### Remove way + +**Trash**:Use OpenList to delete files and then move them to the Recycle Bin. If you delete them accidentally, you can restore them through Thunder Cloud Disk. +**Delete**:Deleting it directly cannot restore it. + +### Use video url + +When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). + +The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## 6. Thunder Browser Expert + +### username、password + +Mobile phone number, email, username, and password used to log in + +- When filling in the mobile phone number, please bring the `+86` area code, for example `+8613822334455` + +### CaptchaToken + +It will be filled automatically without filling in manually +
+ +### Root folder id + +The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it + +- In the request in the package`https://x-api-pan.xunlei.com/drive/v1/files?parent_id&page_token&space=`,you can get the following parameters + - `文件夹ID(id)` + - `文件夹名称(name)` + - `父文件夹ID(parent_id)` +- The `folder ID` obtained in the root directory (for example : `来自分享`、`超级保险箱`), **This will change with different account numbers, there is no the same value, In the request in the package** + ![xunlei](/img/drivers/xunlei/x_br_foled.png) + +### Safe password + +Thunder Browser Safe password + +- Files in Safe password can only be deleted directly and cannot be deleted to the recycle bin, so the [**Deletion method**](#remove-way-1) below has nothing to do with this configuration. + +### Remove way + +**Trash**:Use OpenList to delete files and then move them to the Recycle Bin. If you delete them accidentally, you can restore them through Thunder Cloud +**Delete**:Deleting it directly cannot restore it. + +### Login Type + +- `User`: Only fill in the username and password +- `Refresh token`: Just fill in`RefreshToken` + +### Sign Type + +- `Algorithms`:Choose `Algorithms` Just fill in `Algorithms` (It has been automatically filled. You do n’t have to fill in it yourself. You only need to fill in the username and password.When the algorithm is not invalidated) +- `Captcha sign`: Choose `Captcha sign` Just fill in `Captcha sign` and `Timestamp` + +### Part of parameter Packet Capture description + +- `Captcha token`:No need to fill in +- `Device id`:The value calculated by MD5 is used to judge the login device +- `Client id`, `Client secret`, `Client version`, `Package name`:It related to the signature, fill in according to the actual situation + +--- + +- `User agent`:API requests `User agent`, setting errors may not access or speed limit +- `Download user agent`:When downloading, use the `user agent `,If the setting error cannot be downloaded (the agent will use it) + - `User agent` and `Download user agent`:You can fill it out by yourself. If you don’t know how to fill it out, you can leave it blank and it will be automatically filled in. + +--- + +In the request in the package `https://xluser-ssl.xunlei.com/v1/shield/captcha/init`,you can get the following parameters6 + +- `Client id`、`Device id`、`Captcha sign` +- `Package name`、`Client version`、`Timestamp` + ![xunlei](/img/drivers/xunlei/x_br_add1.png) + + In the request in the package `https://xluser-ssl.xunlei.com/v1/auth/signin/token`, you can get the following parameters3 + - `Client id`、`Client secret`、`Refresh token` + + ![xunlei](/img/drivers/xunlei/x_br_add2.png) + +### Use video url + +When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). + +The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/uc.md b/pages/en/guide/drivers/uc.md new file mode 100644 index 000000000..7f6cf7d67 --- /dev/null +++ b/pages/en/guide/drivers/uc.md @@ -0,0 +1,85 @@ +--- +top: 683 +categories: + - guide + - drivers +--- + +# UC / TV + + + +## UC Cloud + +**https://drive.uc.cn/** + +### Cookie + +Press F12 to open "Debug", select "Network", select any request on the left, and find the one with the `Cookie` parameter. +![](/img/drivers/uc/uc_cookie.png) + +### Root Folder ID + +Root Folder ID is `0` + +- After entering the folder, get the directory ID in the top address bar. If the subdirectory is deeper, the directory ID will be at the back of the address bar. Just write the subdirectory ID you want to mount. + ![](/img/drivers/uc/uc_fileid.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` + +## UC TV + +The TV version supports `302`, but only `List` and `Download` operations are supported. Other operations are not supported (the interface does not support it). + +### Add method + +1. Select the `UC TV` driver, fill in the mounting path, and then save +2. Return to the all driver page and use the mobile APP to scan the QR code (If the QR code is not displayed, click on `Table Layout` in the upper right corner of the driver to switch from list mode to table mode) +3. After scanning the QR code to confirm, disable the driver, then enable the `driver` to use it. + - `Refresh token`、`Device id`、`Query token`,It will be filled in automatically, no manual filling is required - Please do not edit manually and modify it + ![](/img/drivers/tv_qrcode.png) + +### Root Folder ID + +Root Folder ID is `0` + +- After entering the folder, get the directory ID in the top address bar. If the subdirectory is deeper, the directory ID will be at the back of the address bar. Just write the subdirectory ID you want to mount. + ![](/img/drivers/uc/uc_fileid.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/url_tree.md b/pages/en/guide/drivers/url_tree.md new file mode 100644 index 000000000..d53b55613 --- /dev/null +++ b/pages/en/guide/drivers/url_tree.md @@ -0,0 +1,248 @@ +--- +top: 998 +categories: + - guide + - drivers +--- + +# UrlTree + +:::tip +**`地址树-UrlTree`** 是什么? 是用来挂载单个文件链接的,详情可以查看 **→→→[原需求说明](https://github.com/alist-org/alist/issues/3268)←←←** +::: + +## 可写 + +打开后可以对此驱动进行`新建文件夹`、`移动`、`重命名`、`复制`、`删除`、`PutURL` + +- 当用户在实现该接口的驱动中尝试使用SimpleHttp进行离线下载时,将不会调用离线下载工具。 相反,将调用驱动程序的 `PutURL` 方法。 +- **https://github.com/AlistGo/alist/pull/7779** + +
+ +## 用法 + +具体的用法大概讲解一下,建议将下面不同用法都看看越往后越进阶玩法 + +别看写的看起来很难很啰嗦实际上自己操作一下秒懂。 + +1. 最简单的用法,直接写链接即可 + +**输入** + +``` +http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg +https://raw.githubusercontent.com/OpenListTeam/OpenList/main/README_cn.md +``` + +**输出** + +``` +│ aff9c9c8817d943d6857a73188958d45.jpeg +│ README_cn.md +``` + +2. 有文件的同时,也可以创建文件夹第一个代码示例,也可以创建多个文件夹第二个代码示例 + +**输入** + +``` +http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg +folder: + http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg + https://raw.githubusercontent.com/OpenListTeam/OpenList/main/README_cn.md +``` + +``` +http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg +folder: + http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg + https://raw.githubusercontent.com/OpenListTeam/OpenList/main/README_cn.md +http://pic.rmb.bdstatic.com/bjh/a66923e9bfefd03c2998ee998d3b55e7.jpeg +folder2: + http://pic.rmb.bdstatic.com/bjh/e919290bf3b8396817d369cedf33b8cd.jpeg + https://raw.githubusercontent.com/OpenListTeam/OpenList/main/CONTRIBUTING.md +``` + +**输出** + +``` +│ aff9c9c8817d943d6857a73188958d45.jpeg +└─folder + aff9c9c8817d943d6857a73188958d45.jpeg + README_cn.md +``` + +``` +│ aff9c9c8817d943d6857a73188958d45.jpeg +├─folder +│ aff9c9c8817d943d6857a73188958d45.jpeg +│ README_cn.md +│ +├─ a66923e9bfefd03c2998ee998d3b55e7.jpeg +│ +└─folder2 + CONTRIBUTING.md + e919290bf3b8396817d369cedf33b8cd.jpeg +``` + +开头要空两个空格~ + +3. 可以将文件资源二次命名,实际上下载的还是原来的,并且可以填写文件大小 + +格式:`新命名的名字.后缀:文件大小:链接.后缀` + +``` +新名称.jpeg:1024:http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg +新名称2.md:2048:https://raw.githubusercontent.com/OpenListTeam/OpenList/main/README_cn.md +``` + +文件大小不用写也是可以重新命名的 + +``` +新名称.jpeg:http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg +新名称2.md:https://raw.githubusercontent.com/OpenListTeam/OpenList/main/README_cn.md +``` + +@tab 4 + +4. 多个层级目录套娃玩法 + +- 第一层目录文件夹不需要空格,但是第一层目录里面的文件需要空两个空格 + +``` +folder: + http://pic.rmb.bdstatic.com/bjh/aff9c9c8817d943d6857a73188958d45.jpeg + https://raw.githubusercontent.com/OpenListTeam/OpenList/main/README_cn.md +``` + +- 如果你写好第一层了还想在第一个文件夹里面再套一个那就在这个文件夹下面套一层,写法如下,很简单 + - 目录进行空两格,文件加两个空格就是四个,如果继续想往下套一层继续加2个空格即可 + +``` +folder11: + http://pic.rmb.bdstatic.com/bjh/111.jpeg + folder111: + http://pic.rmb.bdstatic.com/bjh/111.jpeg + folder1111: + http://pic.rmb.bdstatic.com/bjh/111.jpeg + folder222: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + folder2222: + http://pic.rmb.bdstatic.com/bjh/222.jpeg +folder22: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + folder222: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + folder2222: + http://pic.rmb.bdstatic.com/bjh/2223.jpeg +folder33: + http://pic.rmb.bdstatic.com/bjh/333.jpeg + folder333: + http://pic.rmb.bdstatic.com/bjh/333.jpeg + folder3333: + http://pic.rmb.bdstatic.com/bjh/333.jpeg +``` + +- 不建议的写法,像 **`2.jpeg,5.jpeg`** 这样写法,建议想往哪一层文件夹就去哪一层文件夹按照格式增加链接,不要像下面这样写 + +**错误写法** + +``` +folder22: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + folder222: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + folder2222: + http://pic.rmb.bdstatic.com/bjh/2222.jpeg + folder22222: + http://pic.rmb.bdstatic.com/bjh/2222.jpeg + folder222222: + http://pic.rmb.bdstatic.com/bjh/2222.jpeg + http://pic.rmb.bdstatic.com/bjh/2.jpeg + http://pic.rmb.bdstatic.com/bjh/5.jpeg +``` + +**正确写法** + +像 **`2.jpeg,5.jpeg`** 这样文件添加时候,添加到对应的目录里面去,分别对应的是 `folder222 和 folder22222` 两个文件夹 + +``` +folder22: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + folder222: + http://pic.rmb.bdstatic.com/bjh/222.jpeg + http://pic.rmb.bdstatic.com/bjh/2.jpeg + folder2222: + http://pic.rmb.bdstatic.com/bjh/2222.jpeg + folder22222: + http://pic.rmb.bdstatic.com/bjh/2222.jpeg + http://pic.rmb.bdstatic.com/bjh/5.jpeg + folder222222: + http://pic.rmb.bdstatic.com/bjh/2222.jpeg +``` + +5. 全部整合起来的举例用法,有常规的,有分类,有重命名,有标注文件大小 + +``` +https://jsd.example.com/gh/OpenListTeam/OpenList/README.md +文档: + Readme.md:https://pic.rmb.bdstatic.com/bjh/说明.md + Doc: + 文档.doc:https://pic.rmb.bdstatic.com/bjh/测试文档.doc + pdf: + pdf2.pdf:https://pic.rmb.bdstatic.com/bjh/测试.pdf + Excel: + 表格3.xlsx:https://pic.rmb.bdstatic.com/bjh/测试表格.xlsx +视频: + https://pic.rmb.bdstatic.com/bjh/222.jpeg + 电视剧: + https://pic.rmb.bdstatic.com/bjh/222.pm4 + 电影: + https://pic.rmb.bdstatic.com/bjh/222.flv +音乐: + https://pic.rmb.bdstatic.com/bjh/222.jpeg + 古典: + https://pic.rmb.bdstatic.com/bjh/222.mp3 + 轻音乐: + https://pic.rmb.bdstatic.com/bjh/222.flac + https://pic.rmb.bdstatic.com/bjh/222.wav +软件: + https://pic.rmb.bdstatic.com/bjh/222.jpeg + PJ: + xx软件.exe:252525:https://pic.rmb.bdstatic.com/bjh/222.exe + xx安装包.zip:259555:https://pic.rmb.bdstatic.com/bjh/222.zip + IPA: + GPT.ipa:https://pic.rmb.bdstatic.com/bjh/222.ipa + APK: + P图.apk:https://pic.rmb.bdstatic.com/bjh/222.apk +``` + +## 提示 + +1. 如果你点击下载发现没有跳转到下载而是预览,这是因为是根据你原本链接来决定的,例如你原本的链接就是下载他就会跳转到下载 +2. 如果你一个文件夹内有相同的文件,会合并只显示一个,默认使用预览查看下载的是你靠上(前)也就是顶部的文件 + - Tips:请勿同时在一个目录内新建两个同名的文件/夹哦~就像你在资源管理器新建两个一样的文件/夹 都会提醒你不能重复 +3. 如果你在添加时在同级目录下写了两个相同的文件夹名字,会合并成一个文件夹但是合并后靠后(下)的文件夹的文件不会显示,但是可以通过浏览器地址栏访问(参考上一条的提示) +4. 排序默认是按照你填写从上到下依次排序的和 `OpenList-别名`的一样 + +### 默认使用的下载方式 + +```mermaid +--- +title: 默认使用的哪种下载方式? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|默认|a2[用户设备] + classDef someclass fill:#f96 + c1[本机代理]-.备选.->a2[用户设备] + b1[代理URL]-.备选.->a2[用户设备] + click a1 "../drivers/common.html#webdav-策略" + click b1 "../drivers/common.html#webdav-策略" + click c1 "../drivers/common.html#webdav-策略" +``` diff --git a/pages/en/guide/drivers/uss.md b/pages/en/guide/drivers/uss.md new file mode 100644 index 000000000..33fc787f6 --- /dev/null +++ b/pages/en/guide/drivers/uss.md @@ -0,0 +1,77 @@ +--- +top: 894 +categories: + - guide + - drivers +--- + +# UPYUN Storage Service + +::: tip +When using the **302 redirect** strategy, the `_upd` and `_upt` parameters are automatically appended to the URL. If **"Parameter Follow"** is enabled in the Upyun console, the CDN nodes will treat these parameters as part of the file path, resulting in a **404 Not Found** error for existing files. + +Solutions: + +- Method 1: In the Upyun console, under **"Cache Control"**, change **"Parameter Follow"** to **"Parameter Not Follow"**. + +- Method 2: Set the WebDAV policy of OpenList to **"Local Proxy"**. This mode avoids appending interfering parameters but will increase the server's data transfer. + ::: + +UPYUN Storage Service,refer to as USS,[**Yupyun USS Official Website**](https://console.upyun.com/services/file/) + +### BucKet + +UPYUN Storage Service Name + +### Endpoint + +Accelerated domain name (default TEST domain or bound domain, not CNAME domain) +If you use http protocol, please add `http://` protocol header. +The test domain name provided by upyun is not accessible in some network and does not support https, so it is recommended to use your own bound domain name. + +### Operator name + +Operator name + +### Operator password + +Operator password + +### Root folder path + +Root path, if not filled, it defaults to the root directory. + +### Sign url expire + +The validity period of the signed download address is 4 hours by default. + +## Example of filling in details + +::: tip +If you want to use the official test domain name, you must manually add http For example: http://xxx.test.upcdn.net +If you want to use HTTPS, of course, you can also add your own domain name, for example: https://you.xxx.com +permission is turned on by itself, and the read permission must be turned on! +::: + +![s3](/img/drivers/s3/up.png) + +### The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/webdav.md b/pages/en/guide/drivers/webdav.md new file mode 100644 index 000000000..ccfa381dd --- /dev/null +++ b/pages/en/guide/drivers/webdav.md @@ -0,0 +1,85 @@ +--- +top: 899 +categories: + - guide + - drivers +--- + +# WebDav + +## Address + +WebDAV root address + +## Username + +username + +## Password + +password + +## Root folder path + +The path of fodler you want to mount, same as join in address + +## Tls insecure skip verify + +Whether to skip SSL certificate verification. If your WebDAV server uses a self-signed certificate, you may need to enable this option. Enabling it will reduce security, please use it with caution. + +## Support 302 redirect + +In general, WebDAV servers return the file content directly. Due to the need for authorization, only proxy downloads are possible. However, some servers redirect to the actual file address, such as OpenList `WebDav Policy`'s `302 Redirect`. + +The `Web Proxy` option in WebDAV storage settings is enabled by default. If disabled, OpenList will attempt to obtain the redirected address and return it to the user device for direct download. + +Prerequisites: + +1. The WebDAV server must support returning 302; if it returns 200, this feature cannot be used, and disabling Web Proxy will make it unusable. +2. The returned 302 address must be publicly accessible and must not require authorization information; otherwise, the user device cannot download it. +3. Disable the `Web Proxy` option in the storage settings. + +## OneDrive/SharePoint + +Select the vendor as sharepoint, and support the international version/21Vianet. + +For OneDrive/SharePoint, you can obtain the WebDAV root address through [this tool](https://tool.example.com/onedrive/webdav). If you want to mount the specified directory, you can join it later. + +The username is the OneDrive account email, and the password is the OneDrive account password. + +## Error message + +- **failed get objs: failed to list objs: PROPFIND/根目录:403** + + Need to log in to [Entra ID](https://entra.microsoft.com/#view/Microsoft_AAD_IAM/TenantOverview.ReactView?Microsoft_AAD_IAM_legacyAADRedirect=true) + + Find 'Manage Security Default' and click to disable it(❗ Note: Closing this option will disable the Authenticator verification for the domain) + + ![webdav](/img/drivers/webdav/webdav_Security.png) + + Another situation is that the corresponding OneDrive account has not been used for a long time, which will also prompt this problem. Try to log in to the account again from the OneDrive web page. The system will prompt you to change the password. After updating the password, try again with the changed password. + +- **failed link: failed get link: redirect failed, status: 200** + + This error indicates that the WebDAV server does not support 302 redirects. You need to enable the `Web Proxy` option in the storage settings to use proxy downloads. + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[local proxy]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[302]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/weiyun.md b/pages/en/guide/drivers/weiyun.md new file mode 100644 index 000000000..b3db39498 --- /dev/null +++ b/pages/en/guide/drivers/weiyun.md @@ -0,0 +1,60 @@ +--- +top: 682 +categories: + - guide + - drivers +--- + +# Tencent weiyun + + + +Tencent Weiyun official website: **https://www.weiyun.com** + +::: tip Weiyun login validity period reminder + +- QQ: The login can only keep alive for 48 hours at present, and the cookie has to be replaced +- WeChat: no such problem, use token + It is recommended to use WeChat to log in + +::: + +## Root folder file_id + +1. Display the root directory, **`leave blank`**, you don’t need to fill in, the program will automatically fill in +2. If only a single folder ID is displayed, select the folder to be displayed, Copy the ID behind the top address bar + + ![](/img/drivers/weiyun/weiyun_fl_id.png) + +## Cookie + +After logging in to Weiyun, **Open the developer debugging tool (F12)**, find the request that carries the cookie in any request, copy and fill in it. + +- The token obtained by WeChat login is longer than the cookie field of QQ login +- QQ WeChat fills in the `Cookie` field value, and you can see it by just looking for a request + + ![](/img/drivers/weiyun/weiyun_cookie.png) + +## Other notes + +1. The copy function cannot be used +2. **It is recommended not to share Weiyun with the outside world. After all, it is bound to your own QQ number. If you have a major loss to QQ or other things after being blocked/frozen, you will be responsible for the consequences** + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + c1 + end + c1[local proxy]:::someclass==default===>a2[user equipment] + classDef someclass fill:#f96 + b1[Download proxy URL]-.alternative.->a2[user equipment] + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/wopan.md b/pages/en/guide/drivers/wopan.md new file mode 100644 index 000000000..018be2240 --- /dev/null +++ b/pages/en/guide/drivers/wopan.md @@ -0,0 +1,78 @@ +--- +top: 693 +categories: + - guide + - drivers +--- + +# WoPan + + + +[https://pan.wo.cn/](https://pan.wo.cn/) + +## GetToken + +::: tip +The difference between the two token acquisition methods: + +- Method 1: **Token valid for seven days** Logging in to the web version of China Unicom Cloud Drive will disconnect and invalidate the mount point on OpenList's side. However, logging in to the mobile version is fine, and thereby you can have both open simultaneously. + +- Method 2: **Token valid for two months** There isn't any problem to log into the Unicom Cloud Drive. However, it will be kicked off if you log in on the mobile side. + +::: + +### Method 1 + +1. Open developer tools +2. Open the official website to log in +3. Find the request with this content: + ![WoPan token request screenshot](/img/drivers/wopan/wopan-req.png) +4. Find the token in the response: + ![WoPan token response screenshot](/img/drivers/wopan/wopan-resp.png) + +### Method 2 + +1. Open developer tools +2. Open the official website to login +3. Find the Apps tab in Developer Tools and select Session Storage: + ![WoPan session storage screenshot](/img/drivers/wopan/wopan-session-storage.png) +4. Follow the arrows in the diagram to find the `Refresh Token` and the `Access Token` + +## Root folder ID + +- **Personal cloud:**:**0** + - Single folder ID:Unknown (wait for replenishment) +- **Family cloud**:Unknown (wait for replenishment) + - Family cloud Single folder ID:Unknown (wait for replenishment) + +## Type + +Personal cloud:Put the `family ID` blank is the personal cloud +Family cloud:add `Family ID` Log in to the [SmartHome] -> [我的] -> [您的家], click"+" [Invite] -> [微信邀请], send the link to yourself, open it and copy all the text after 'groupId='. + +## OpenList fill in examples: + +Data obtained by using tools `Refresh_token Fill in the refresh token`, `Access_token fills in access_token` +![WoPan add example screenshot](/img/drivers/wopan/add-wopan.png) + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/drivers/wps.md b/pages/en/guide/drivers/wps.md new file mode 100644 index 000000000..4118a2617 --- /dev/null +++ b/pages/en/guide/drivers/wps.md @@ -0,0 +1,48 @@ +--- +top: 675 +categories: + - guide + - drivers +--- + +# WPS + + + +OpenList supports mounting WPS cloud document (which is also called KDocs) web. + +Official websites: + +- KDocs (Personal): +- WPS 365 (Business/Enterprise/Education): + +## Cookie + +::: warning +Please make sure to obtain the Cookie using a new browser environment or incognito mode to avoid including session information from other accounts. + +A session can only be logged in to one account at a time. +::: + +The cookie of WPS cloud storage can be obtained by following the steps below: + +1. Create a new browser environment or open an incognito window. +2. Visit the corresponding WPS cloud document website and log in to your WPS account. +3. Open the developer tools (press F12 or right-click and select "Inspect"). +4. Go to the "Network" tab and refresh the page. +5. Search for `islogin` in the network requests and open one of them. +6. In the request details, find the "Headers" section and look for the `Cookie` and `User-Agent` fields. +7. Copy the entire value of relevant fields and use them in OpenList. + +## Mode + +There are two modes of API: + +- Personal +- Business + +Please select the appropriate mode based on your account type when adding the WPS cloud storage in OpenList. + +## Custom UA + +You can set a custom User-Agent for requests to WPS cloud document. diff --git a/pages/en/guide/drivers/yandex.md b/pages/en/guide/drivers/yandex.md new file mode 100644 index 000000000..20b2841d9 --- /dev/null +++ b/pages/en/guide/drivers/yandex.md @@ -0,0 +1,37 @@ +--- +top: 589 +categories: + - guide + - drivers +--- + +# Yandex Disk + +## Refresh token + +[Click here](https://oauth.yandex.com/authorize?response_type=code&client_id=a78d5a69054042fa936f6c77f9a0ae8b) to get the refresh token. + +## Root folder path + +The root foler to mount, defaults to `/` + +## The default download method used + +```mermaid +--- +title: Which download method is used by default? +--- +flowchart TB + style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff + style a2 fill:#ff7575,stroke:#333,stroke-width:4px + subgraph ide1 [ ] + a1 + end + a1[302]:::someclass====|default|a2[user equipment] + classDef someclass fill:#f96 + c1[local proxy]-.alternative.->a2[user equipment] + b1[Download proxy URL]-.alternative.->a2[user equipment] + click a1 "../drivers/common.html#webdav-policy" + click b1 "../drivers/common.html#webdav-policy" + click c1 "../drivers/common.html#webdav-policy" +``` diff --git a/pages/en/guide/index.md b/pages/en/guide/index.md new file mode 100644 index 000000000..55aca17b4 --- /dev/null +++ b/pages/en/guide/index.md @@ -0,0 +1,56 @@ +--- +categories: + - guide +top: 100000 +--- + +# Quick Start + +OpenList is a file list program that supports multiple storage🗂️, a resilient, long-term governance, community-driven fork of AList🔀 — built to defend open source against trust-based attacks🛡️. + +You can follow the steps below to quickly get started with OpenList 🚀: + +::: info Still using Alist V3? +Here's your complete migration guide from Alist V3 to OpenList: + +[🚀 Migration Guide](/en/guide/migrate) +::: + +## Installation 🖥️ + +- [One-click Script 🎯](/en/guide/installation/script) +- [Manual installation ⚙️](/en/guide/installation/manual) +- [Use Docker 🐋](/en/guide/installation/docker) +- [Use 1Panel 📟](/en/guide/installation/1panel) +- [Use TrueNAS Scale 🗄️](/en/guide/installation/truenas) +- [Use Koyeb ☁️](/en/guide/installation/koyeb) +- [Desktop 💻](/en/guide/installation/desktop) +- [PaaS ☁️](/en/guide/installation/paas) +- [Run from source 🏗️](/en/guide/installation/source) +- [Download ⬇️](/en/guide/installation/download) +- [Reverse proxy 🔄](/en/guide/installation/reverse-proxy) + +## Storage Setup 💾 + +- [Common 🗂️](/en/guide/drivers/common) + +## Configuration ⚙️ + +- [Configuration 📝](/en/configuration/configuration) +- [Site Configuration 🌐](/en/configuration/site) +- [Style Configuration 🎨](/en/configuration/style) +- [Preview Configuration 👀](/en/configuration/preview) +- [Global Configuration 🌍](/en/configuration/global) +- [Other Configuration 🔧](/en/configuration/other) +- [Side Configuration 📐](/en/configuration/side) + +## FAQ ❓ + +- [OpenList Error-Code 🚨](/en/faq/error) +- [How to 🛠️](/en/faq/howto) +- [Why 🤔](/en/faq/why) +- [See also 🔍](/en/faq/seealso) + +## Ecosystem 🏞️ + +- [See more 🔍](/en/ecosystem/) diff --git a/pages/en/guide/installation/1panel.md b/pages/en/guide/installation/1panel.md new file mode 100644 index 000000000..fce1a686d --- /dev/null +++ b/pages/en/guide/installation/1panel.md @@ -0,0 +1,78 @@ +--- +top: 45 +categories: + - guide + - installation +--- + +# Use 1Panel + +## Install + +### Install 1Panel + +First, install 1Panel on your server. + +Run the following **one-click installation script** as the **root user** to automatically download and install 1Panel: + +```bash +bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)" +``` + +> 📖 **Detailed Installation Guide**: Please refer to the [1Panel Official Installation Documentation](https://1panel.cn/docs/v2/installation/online_installation/) + +After installation, log in to 1Panel using the provided **access address** and **initial account credentials**. + +### Install OpenList + +Log in to 1Panel, go to the **App Store**, search for **openlist**, and click **Install**. + +![search](/img/1panel/search.png) + +> During installation, please configure the following parameters according to actual needs: +> +> - **Version**: Select the latest stable version +> - **WebUI Port**: Default is `5244`, can be modified as needed +> - **S3 Port**: Default is `5246`, can be modified as needed +> - **Pre-installed Environment**: +> - `Thumbnail`: Pre-install ffmpeg +> - `Offline Download`: Pre-install aria2 +> - `All of the above`: Pre-install ffmpeg & aria2 +> - **Timezone**: Recommended to set to `Asia/Shanghai` +> - **Advanced Settings**: Be sure to check **External Port Access** + +> Keeping the **default configuration** can also complete the installation, but it is recommended to adjust according to actual needs. + +![install](/img/1panel/install.png) + +### Use OpenList + +After installation, go to the **Installed** page and click **Open** to access the OpenList **WebUI**. + +![installed](/img/1panel/installed.png) + +> It is recommended to set the **Default Access Address** in the panel settings before using the application. + +> If you later configure a **reverse proxy**, update the **Web Access Address** in `Installed → Parameters`. + +### Get Default Account & Password + +Go to the **Container List**, find the **OpenList container**, and click **Terminal**. Then run the following commands inside the container: + +- **Generate a random password**: + + ```bash + ./openlist admin random + ``` + +- **Manually set a password**: + + ```bash + ./openlist admin set NEW_PASSWORD + ``` + +> **Note**: Replace `NEW_PASSWORD` with your desired password. + +![container](/img/1panel/container.png) + +![terminal](/img/1panel/terminal.png) diff --git a/pages/en/guide/installation/desktop.md b/pages/en/guide/installation/desktop.md new file mode 100644 index 000000000..c289b3a61 --- /dev/null +++ b/pages/en/guide/installation/desktop.md @@ -0,0 +1,225 @@ +--- +top: 40 +categories: + - guide + - installation +comment: false +--- + +# Desktop + +## What is OpenList Desktop + +OpenList Desktop is a cross-platform desktop application that provides a user-friendly interface for managing OpenList and performing local mounts via Rclone. Built with Vue 3, Tauri, and Rust, it serves as a comprehensive solution for: + +- Managing OpenList core +- Mounting and managing cloud storage (WebDAV) +- Monitoring service status and uptime +- Providing system tray integration for background operations + +**Repository**: [OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) + +## Features + +### Core Features + +- **OpenList Service Management**: Start, stop, and monitor OpenList core +- **Local Mounting**: Mount via Rclone to the local file system +- **Real-time Monitoring**: Track service status, uptime, and performance metrics +- **Log Management**: View and manage OpenList and Rclone logs +- **System Tray**: Background operation with system tray notifications + +### Management Features + +- **Service Control**: Start/stop/restart OpenList and Rclone services +- **Configuration Management**: GUI-based configuration +- **Log Monitoring**: Real-time log viewing and management +- **Update Management**: Automatic update checking and installation +- **Auto-startup**: Configure applications to start with system boot + +## Screenshots + +### Home Dashboard + +![dashboard](/img/desktop/dashboard.png) + +The main dashboard provides a comprehensive overview of your OpenList Desktop environment with: + +- OpenList backend status monitoring +- Quick action buttons for common tasks +- OpenList and Rclone version management +- Service management controls + +### Mount Management + +![mount](/img/desktop/mount.png) + +Easily perform local mounts: + +- Add and configure storage remotes +- Mount/unmount cloud storage +- Monitor mount status and statistics +- Configure auto-mounting options + +### Log Management + +![logs](/img/desktop/logs.png) + +Manage logs of multiple sources: + +- OpenList logs +- Rclone logs +- Application logs +- Filter and search logs +- Real-time log updates +- Export logs to file and clipboard + +## Installation + +### System Requirements + +- **Operating System**: Windows 10+, macOS 10.15+, or Linux (Ubuntu 18.04+) + +### Download Options + +#### GitHub Releases (Recommended) + +Download the latest release from [GitHub Releases](https://github.com/OpenListTeam/OpenList-Desktop/releases): + +- **Windows**: `OpenList-Desktop_x.x.x_{arch}-setup.exe` +- **macOS**: `OpenList-Desktop_x.x.x_{arch}.dmg` +- **Linux**: `OpenList-Desktop_x.x.x_{arch}.deb` or `OpenList-Desktop_x.x.x_{arch}.rpm` + +#### Using Package Managers + +##### Windows - Winget + +```bash +winget install OpenListTeam.OpenListDesktop +``` + +### Installation Steps + +#### Windows + +1. Download the `.exe` installer +2. Run the installer as Administrator +3. Follow the installation wizard +4. Launch from Start Menu or Desktop shortcut + +#### macOS + +1. Download the `.dmg` file +2. Open the DMG and drag OpenList Desktop to Applications +3. Right-click and select "Open" (first time only) +4. Grant necessary permissions when prompted + +#### Linux + +1. Download the `.deb` or `.rpm` package +2. Use your package manager to install: + +```bash +# For Debian/Ubuntu +sudo dpkg -i OpenList-Desktop_x.x.x_amd64.deb + +# For CentOS/RHEL/Fedora +sudo rpm -i OpenList-Desktop_x.x.x_amd64.rpm +``` + +## Getting Started + +### First Launch + +::: tip +It is recommended to run OpenList Desktop with Administrator privileges on first launch to ensure proper service installation and configuration. +::: + +1. **Initial Setup**: On first launch, the application will guide you through initial configuration +2. **Service Installation**: Install the OpenList service when prompted +3. **Storage Configuration**: Configure your first cloud storage connection + +### Basic Operations + +#### Starting Services + +1. Navigate to **Dashboard** tab +2. Click **Start OpenList Core** in Quick Actions +3. Click **Start Rclone Backend** if needed + +#### Adding Cloud Storage + +1. Navigate to **Mount** tab +2. Click **Add Remote** button +3. Configure storage settings: + - **Name**: Unique identifier for the storage + - **Type**: Storage provider (WebDAV) + - **URL**: Storage endpoint URL + - **Credentials**: Username and password + - **Mount Point**: Local directory path +4. Click **Save** and **Mount** + +#### Monitoring Operations + +- **Service Status**: Check service health indicators on the dashboard +- **Logs**: Use the Logs tab to monitor system operations +- **Performance**: View uptime and response metrics on the dashboard + +### Advanced Features + +#### Auto-Mount Configuration + +Configure storages to automatically mount at startup with custom Rclone flags for optimal performance: + +- `--vfs-cache-mode=full`: Enable full VFS caching +- `--buffer-size=256M`: Increase buffer size +- `--transfers=10`: Concurrent transfer limit + +#### System Tray Operations + +- **Right-click tray icon** for quick actions +- **Double-click** to show/hide main window +- **Background operation** with notifications + +## Configuration + +### Application Settings + +Access comprehensive settings management including: + +- **OpenList Core Configuration**: Port, data directory, auto-launch settings +- **Startup Preferences**: Auto-startup and automation options +- **Theme and Language**: UI customization options +- **Update Settings**: Automatic update preferences + +## Troubleshooting + +### Common Issues + +- **Service won't start**: Check if ports are available and run as Administrator +- **Mount fails**: Verify storage credentials and network connectivity +- **Performance issues**: Adjust Rclone cache settings and buffer sizes +- **Update problems**: Check internet connection and proxy settings + +## Build from Source + +For developers who want to build from source: + +```bash +# Clone the repository +git clone https://github.com/OpenListTeam/OpenList-Desktop.git +cd openlist-desktop + +# Install dependencies +yarn install + +# Prepare development environment +yarn run prebuild:dev + +# Development mode +yarn tauri dev + +# Build the application +yarn run build +yarn run tauri build +``` diff --git a/pages/en/guide/installation/docker.md b/pages/en/guide/installation/docker.md new file mode 100644 index 000000000..865dec96f --- /dev/null +++ b/pages/en/guide/installation/docker.md @@ -0,0 +1,567 @@ +--- +top: 50 +categories: + - guide + - installation +--- + +# Use Docker + +::: warning + +- In version `v4.1.0` and later (excluding `v4.1.0`), OpenList has removed the `PUID` and `PGID` environment variables in the image, and has adopted a method similar to that of MariaDB, where a user named `openlist` (UID 1001) and a group named `openlist` (GID 1001) are created, and `openlist server` runs under this user. + + This means you need to manually handle the permission issues of the mapped directory, ensuring that the OpenList user (1001) inside the container has access to the mapped directory. + + You can also run the container with the `--user UID:GID` option to specify the user and group under which OpenList runs inside the container, allowing it to access the mapped directory. + +- In the **rootless** mode of Docker, `--user 0:0` represents the current user's UID and GID. Please ensure that you set the `--user` parameter correctly when running the container to ensure proper file permissions. + ::: + +## Install + +### Docker CLI + +Install Docker. And run the command below: + +#### For version after v4.1.0 + +::: warning +Please note: `/etc/openlist` is just the default mapped directory, you can change it to another directory as needed. +::: + +::: tip +If you are using the current user to run and manage OpenList and its configuration directory, please use the following command: +::: + +```bash +mkdir -p /etc/openlist +docker run --user $(id -u):$(id -g) -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest +``` + +::: tip +If you want to run and manage OpenList and its configuration directory using the default OpenList user (1001) inside the container, please use the following command: +::: + +```bash +sudo chown -R 1001:1001 /etc/openlist +docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest +``` + +#### For version v4.1.0 and earlier + +```bash +docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e PUID=0 -e PGID=0 -e UMASK=022 --name="openlist" openlistteam/openlist:latest +``` + +### Docker Compose + +Create `docker-compose.yml` file. + +```bash +mkdir -p /opt/openlist +cd /opt/openlist +vim docker-compose.yml +``` + +Write the content below. Then save and exit. + +#### For version after v4.1.0 + +```yaml +# docker-compose.yml +services: + openlist: + image: 'openlistteam/openlist:latest' + container_name: openlist + user: '0:0' # Please replace `0:0` with the actual user ID and group ID you want to use to run OpenList. + volumes: + - './data:/opt/openlist/data' + ports: + - '5244:5244' + environment: + - UMASK=022 + restart: unless-stopped +``` + +#### For version v4.1.0 and earlier + +```yaml +# docker-compose.yml +services: + openlist: + image: 'openlistteam/openlist:latest' + container_name: openlist + volumes: + - './data:/opt/openlist/data' + ports: + - '5244:5244' + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + restart: unless-stopped +``` + +Run commands in the same path of `docker-compose.yml` file: + +```bash +docker compose pull +docker compose up -d +``` + +## Env + +| Name | Default | Desc | +| :------------------------ | :------ | -------------------------------------------------------------------------------------------------------------------------- | +| `PUID` | `0` | User UID, Deprecated in v4.1.0 later versions | +| `PGID` | `0` | User GID, Deprecated in v4.1.0 later versions | +| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | +| `TZ` | `UTC` | Default is the UTC time zone. If you want to specify a time zone, you can set this variable, for example: `Asia/Shanghai`. | +| `RUN_ARIA2` | | Whether to run ARIA2 concurrently, default is `true` if aria2 is pre-installed, otherwise it is `false`. | +| `OPENLIST_ADMIN_PASSWORD` | | Set the password of admin by environment variable | + +Additionally, OpenList supports passing [configuration](/en/configuration/configuration) through environment variables. The OpenList in the Docker image runs by default with the `--no-prefix` flag, so you don't need to add the `OPENLIST_` prefix. + +You can view all available environment variables online in Go Packages. + +https://pkg.go.dev/github.com/OpenListTeam/OpenList/v4/internal/conf#Config + +## Image Versions + +- Stable version: `openlistteam/openlist:latest` or `openlistteam/openlist:v*.*.*` + - Latest image version tag, please refer to https://hub.docker.com/r/openlistteam/openlist/tags + + - Some PaaS platforms do not support images larger than 100MB. Please use the lightweight image `lite`, for example: `openlistteam/openlist:latest-lite`. Otherwise, you may encounter the following error: `Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.` + +- Dev version: `openlistteam/openlist:beta` + +Pre-installed environment image suffix: + +| Suffix | Desc | +| :------- | ----------------------------------------------------------------------- | +| `aio` | An image that includes all of the following pre-installed environments. | +| `ffmpeg` | Pre-installed FFmpeg image for generating thumbnail for local storage | +| `aria2` | Pre-installed aria2 image for offline downloading. | + +You can append a suffix using the `-` symbol after any of the mirror tags to switch to an image with the corresponding environment. For example, `openlistteam/openlist:latest-aio` `openlistteam/openlist:latest-aria2` `openlistteam/openlist:latest-ffmpeg`. + +--- + +If the thumbnail generation function still does not work when using the pre-installed ffmpeg, please confirm: + +- You are using local storage +- Switched to grid view +- The thumbnail switch in local storage driver settings is enabled +- The configuration path for the thumbnail cache folder in local storage is correct, for example, `data/thumbnail` + +--- + +When using a pre-installed aria2 mirror, you might see errors like the following in the OpenList logs: + +``` +ERRO[2022-11-20 12:05:19] error [unaligned 64-bit atomic operation] while run task [download http://xxx.com/xxx.png to [/ftp](/)] +``` + +The solution is, if the CPU architecture is 64-bit, you can try to manually pull a 64-bit image or rebuild the container. If the CPU architecture is 32-bit, there is currently no available solution. + +## See the admin's info + +### First run + +```bash +docker logs openlist +``` + +You will see the admin password in the log. + +``` +Successfully created the admin user and the initial password is: xYZabHGf +``` + +### Not first run + +You can **randomly generate** or **manually set** + +```bash +# Randomly generate password +docker exec -it openlist ./openlist admin random + +# Manually set password to `NEW_PASSWORD` (replace this) +docker exec -it openlist ./openlist admin set NEW_PASSWORD +``` + +## Update + +### Watchtower + +If you find it troublesome, you can complete the update with a single line using Watchtower. + +```bash +docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower openlist --cleanup --run-once +``` + +If the container name is not named `openlist`, please replace it to the actual one. + +### Docker CLI + +```bash +# View the container (find the ID of the OpenList container) +docker ps -a + +# Stop running OpenList container instance, otherwise it cannot be deleted (this time the ID of the OpenList container is d429749a6e69, it is different for each installation) +docker stop ID + +# Delete the OpenList container (the data is still there as long as you don't delete it manually) +docker rm ID + +# Pull the latest image of OpenList +docker pull openlistteam/openlist:latest +``` + +#### Upgrade to version v4.1.0 and later (excluding v4.1.0) + +::: tip +If you want to run and manage OpenList and its configuration directory using the default OpenList user (1001) inside the container, please use the following command: +::: + +```bash +chown -R 1001:1001 /etc/openlist +docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest +``` + +::: tip +If you are using the current user to run and manage OpenList and its configuration directory, please use the following command: +::: + +```bash +sudo chown -R $(id -u):$(id -g) /etc/openlist +docker run --user $(id -u):$(id -g) -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest +``` + +#### Upgrade to version v4.1.0 and earlier (including v4.1.0) + +```bash +docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e PUID=0 -e PGID=0 -e UMASK=022 --name="openlist" openlistteam/openlist:latest +``` + +### Docker Compose + +Enter the same path of `docker-compose.yml` file and run: + +```bash +docker compose pull +docker compose down +docker compose up -d +``` + +## Advanced Docker Compose + +Create `docker-compose.yml` file. + +```bash +mkdir -p /opt/openlist +vim docker-compose.yml +``` + +Write the content below. Then save and exit. + +#### For version after v4.1.0 + +```yaml +# docker-compose.yml +services: + # OpenList | OpenList Core Service + openlist: + image: 'openlistteam/openlist:latest' + container_name: openlist + volumes: + - '${OPLISTDX_DATA}/openlist:/opt/openlist/data' + - '${OPLISTDX_TEMP}/aria2:/opt/openlist/data/temp/aria2' + - '${OPLISTDX_TEMP}/qBittorrent:/opt/openlist/data/temp/qBittorrent' + - '${OPLISTDX_TEMP}/Transmission:/opt/openlist/data/temp/Transmission' + user: '${OPLISTDX_PUID}:${OPLISTDX_PGID}' # If you are using v4.1.0 later, must uncomment this line and set the correct user ID and group ID. + ports: + - '5244:5244' + environment: + # - PUID=${OPLISTDX_PUID} # Deprecated in v4.1.0 later versions + # - PGID=${OPLISTDX_PGID} # Deprecated in v4.1.0 later versions + - TZ=${OPLISTDX_TZ} + - UMASK=022 + restart: unless-stopped + + # # Aria2 下载器及webui | Aria2 Downloader & WebUI + # aria2-pro: + # image: p3terx/aria2-pro + # container_name: aria2-pro + # restart: unless-stopped + # ports: + # - '6800:6800' + # - '6888:6888' + # - '6888:6888/udp' + # volumes: + # - '${OPLISTDX_DATA}/aria2-pro:/config' + # - '${OPLISTDX_DOWNLOADS}/aria2:/downloads' + # - '${OPLISTDX_TEMP}/aria2:/opt/openlist/data/temp/aria2' + # environment: + # - 'PUID=${OPLISTDX_PUID}' + # - 'PGID=${OPLISTDX_PGID}' + # - 'TZ=${OPLISTDX_TZ}' + # - 'UMASK_SET=022' + # - 'RPC_SECRET=${OPLISTDX_ARIA2TOKEN}' + # - 'RPC_PORT=6800' + # - 'LISTEN_PORT=6888' + # ariang: + # container_name: ariang + # image: p3terx/ariang + # command: --port 6880 + # ports: + # - 6880:6880 + # restart: unless-stopped + # environment: + # - 'PUID=${OPLISTDX_PUID}' + # - 'PGID=${OPLISTDX_PGID}' + # - 'TZ=${OPLISTDX_TZ}' + # logging: + # driver: json-file + # options: + # max-size: 1m + # depends_on: + # - aria2-pro + + # # qBittorrent 下载器 | qBittorrent Downloader + # qbittorrent: + # image: lscr.io/linuxserver/qbittorrent:latest + # container_name: qbittorrent + # environment: + # - PUID=${OPLISTDX_PUID} + # - PGID=${OPLISTDX_PGID} + # - TZ=${OPLISTDX_TZ} + # - WEBUI_PORT=8080 + # - TORRENTING_PORT=6881 + # volumes: + # - '${OPLISTDX_DATA}/qbittorrent:/config' + # - '${OPLISTDX_DOWNLOADS}/qbittorrent:/downloads' + # - '${OPLISTDX_TEMP}/qBittorrent:/opt/openlist/data/temp/qBittorrent' + # ports: + # - 8080:8080 + # - 6881:6881 + # - 6881:6881/udp + # restart: unless-stopped + + # # Transmission 下载器 | Transmission Downloader + # transmission: + # image: lscr.io/linuxserver/transmission:latest + # container_name: transmission + # environment: + # - PUID=${OPLISTDX_PUID} + # - PGID=${OPLISTDX_PGID} + # - TZ=${OPLISTDX_TZ} + # - TRANSMISSION_WEB_HOME=${OPLISTDX_TRANSMISSION_WEB_HOME} #optional + # - USER=${OPLISTDX_TRANSMISSION_USER} #optional + # - PASS=${OPLISTDX_TRANSMISSION_PASS} #optional + # - WHITELIST=${OPLISTDX_TRANSMISSION_WHITELIST} #optional + # - PEERPORT=${OPLISTDX_TRANSMISSION_PEERPORT} #optional + # - HOST_WHITELIST=${OPLISTDX_TRANSMISSION_HOST_WHITELIST} #optional + # volumes: + # - '${OPLISTDX_DATA}/transmission:/config' + # - '${OPLISTDX_DOWNLOADS}/transmission:/downloads' + # - '${OPLISTDX_TRANSMISSIONWATCH}:/watch' + # - '${OPLISTDX_TEMP}/Transmission:/opt/openlist/data/temp/Transmission' + # ports: + # - 9091:9091 + # - 51413:51413 + # - 51413:51413/udp + # restart: unless-stopped +``` + +#### For version v4.1.0 and earlier + +```yaml +# docker-compose.yml +services: + # OpenList | OpenList Core Service + openlist: + image: 'openlistteam/openlist:latest' + container_name: openlist + volumes: + - '${OPLISTDX_DATA}/openlist:/opt/openlist/data' + - '${OPLISTDX_TEMP}/aria2:/opt/openlist/data/temp/aria2' + - '${OPLISTDX_TEMP}/qBittorrent:/opt/openlist/data/temp/qBittorrent' + - '${OPLISTDX_TEMP}/Transmission:/opt/openlist/data/temp/Transmission' + # user: "${OPLISTDX_PUID}:${OPLISTDX_PGID}" # If you are using v4.1.0 later, must uncomment this line and set the correct user ID and group ID. + ports: + - '5244:5244' + environment: + - PUID=${OPLISTDX_PUID} # Deprecated in v4.1.0 later versions + - PGID=${OPLISTDX_PGID} # Deprecated in v4.1.0 later versions + - TZ=${OPLISTDX_TZ} + - UMASK=022 + restart: unless-stopped + + # # Aria2 下载器及webui | Aria2 Downloader & WebUI + # aria2-pro: + # image: p3terx/aria2-pro + # container_name: aria2-pro + # restart: unless-stopped + # ports: + # - '6800:6800' + # - '6888:6888' + # - '6888:6888/udp' + # volumes: + # - '${OPLISTDX_DATA}/aria2-pro:/config' + # - '${OPLISTDX_DOWNLOADS}/aria2:/downloads' + # - '${OPLISTDX_TEMP}/aria2:/opt/openlist/data/temp/aria2' + # environment: + # - 'PUID=${OPLISTDX_PUID}' + # - 'PGID=${OPLISTDX_PGID}' + # - 'TZ=${OPLISTDX_TZ}' + # - 'UMASK_SET=022' + # - 'RPC_SECRET=${OPLISTDX_ARIA2TOKEN}' + # - 'RPC_PORT=6800' + # - 'LISTEN_PORT=6888' + # ariang: + # container_name: ariang + # image: p3terx/ariang + # command: --port 6880 + # ports: + # - 6880:6880 + # restart: unless-stopped + # environment: + # - 'PUID=${OPLISTDX_PUID}' + # - 'PGID=${OPLISTDX_PGID}' + # - 'TZ=${OPLISTDX_TZ}' + # logging: + # driver: json-file + # options: + # max-size: 1m + # depends_on: + # - aria2-pro + + # # qBittorrent 下载器 | qBittorrent Downloader + # qbittorrent: + # image: lscr.io/linuxserver/qbittorrent:latest + # container_name: qbittorrent + # environment: + # - PUID=${OPLISTDX_PUID} + # - PGID=${OPLISTDX_PGID} + # - TZ=${OPLISTDX_TZ} + # - WEBUI_PORT=8080 + # - TORRENTING_PORT=6881 + # volumes: + # - '${OPLISTDX_DATA}/qbittorrent:/config' + # - '${OPLISTDX_DOWNLOADS}/qbittorrent:/downloads' + # - '${OPLISTDX_TEMP}/qBittorrent:/opt/openlist/data/temp/qBittorrent' + # ports: + # - 8080:8080 + # - 6881:6881 + # - 6881:6881/udp + # restart: unless-stopped + + # # Transmission 下载器 | Transmission Downloader + # transmission: + # image: lscr.io/linuxserver/transmission:latest + # container_name: transmission + # environment: + # - PUID=${OPLISTDX_PUID} + # - PGID=${OPLISTDX_PGID} + # - TZ=${OPLISTDX_TZ} + # - TRANSMISSION_WEB_HOME=${OPLISTDX_TRANSMISSION_WEB_HOME} #optional + # - USER=${OPLISTDX_TRANSMISSION_USER} #optional + # - PASS=${OPLISTDX_TRANSMISSION_PASS} #optional + # - WHITELIST=${OPLISTDX_TRANSMISSION_WHITELIST} #optional + # - PEERPORT=${OPLISTDX_TRANSMISSION_PEERPORT} #optional + # - HOST_WHITELIST=${OPLISTDX_TRANSMISSION_HOST_WHITELIST} #optional + # volumes: + # - '${OPLISTDX_DATA}/transmission:/config' + # - '${OPLISTDX_DOWNLOADS}/transmission:/downloads' + # - '${OPLISTDX_TRANSMISSIONWATCH}:/watch' + # - '${OPLISTDX_TEMP}/Transmission:/opt/openlist/data/temp/Transmission' + # ports: + # - 9091:9091 + # - 51413:51413 + # - 51413:51413/udp + # restart: unless-stopped +``` + +Create `.env` file. + +```bash +# ============================================================================= +# 基础配置 | Basic Configuration +# ============================================================================= +# 用户和组 ID(确保文件权限正确)| User and group ID (ensure correct file permissions) +OPLISTDX_PUID=0 +OPLISTDX_PGID=0 + +# 时区设置 | Timezone setting +OPLISTDX_TZ=Asia/Shanghai + +# ============================================================================= +# 路径配置 | Path Configuration +# ============================================================================= +# 主数据目录 | Main data directory +OPLISTDX_DATA=./data + +# 临时文件目录 | Temporary files directory +OPLISTDX_TEMP=./temp + +# # 下载目录 | Downloads directory +# OPLISTDX_DOWNLOADS=./downloads + +# # Transmission 监控目录 | Transmission watch directory +# OPLISTDX_TRANSMISSIONWATCH=./watch + +# # ============================================================================= +# # Aria2 配置 | Aria2 Configuration +# # ============================================================================= +# # Aria2 RPC 密钥 | Aria2 RPC secret token +# OPLISTDX_ARIA2TOKEN=your_aria2_secret_token + +# # ============================================================================= +# # Transmission 配置(可选)| Transmission Configuration (Optional) +# # ============================================================================= +# # Transmission Web UI 主题目录 | Transmission Web UI theme directory +# OPLISTDX_TRANSMISSION_WEB_HOME= + +# # Transmission Web UI 用户名 | Transmission Web UI username +# OPLISTDX_TRANSMISSION_USER=admin + +# # Transmission Web UI 密码 | Transmission Web UI password +# OPLISTDX_TRANSMISSION_PASS=password + +# # IP 白名单(逗号分隔)| IP whitelist (comma separated) +# OPLISTDX_TRANSMISSION_WHITELIST= + +# # Peer 端口 | Peer port +# OPLISTDX_TRANSMISSION_PEERPORT= + +# # 主机白名单 | Host whitelist +# OPLISTDX_TRANSMISSION_HOST_WHITELIST= +``` + +Run commands in the same path of `docker-compose.yml` file: + +```bash +docker compose pull +docker compose up -d +``` + +### Env + +| Name | Default | Desc | +| :------------------------------------- | :------------ | -------------------------------------------------------------------------------------------------------------------------- | +| `OPLISTDX_PUID` | `0` | User UID | +| `OPLISTDX_PGID` | `0` | User GID | +| `OPLISTDX_TZ` | `UTC` | Default is the UTC time zone. If you want to specify a time zone, you can set this variable, for example: `Asia/Shanghai`. | +| `OPLISTDX_DATA` | `./data` | Main data directory. | +| `OPLISTDX_TEMP` | `./temp` | Temporary files directory | +| `OPLISTDX_DOWNLOADS` | `./downloads` | Downloads directory | +| `OPLISTDX_TRANSMISSIONWATCH` | `./watch` | Transmission watch directory | +| `OPLISTDX_ARIA2TOKEN` | | Aria2 RPC secret token | +| `OPLISTDX_TRANSMISSION_WEB_HOME` | | Transmission Web UI theme directory | +| `OPLISTDX_TRANSMISSION_USER` | | Transmission Web UI username | +| `OPLISTDX_TRANSMISSION_PASS` | | Transmission Web UI password | +| `OPLISTDX_TRANSMISSION_WHITELIST` | | IP whitelist (comma separated) | +| `OPLISTDX_TRANSMISSION_PEERPORT` | | Peer port | +| `OPLISTDX_TRANSMISSION_HOST_WHITELIST` | | Host whitelist | diff --git a/pages/en/guide/installation/download.md b/pages/en/guide/installation/download.md new file mode 100644 index 000000000..37a754a02 --- /dev/null +++ b/pages/en/guide/installation/download.md @@ -0,0 +1,12 @@ +--- +categories: + - guide + - installation +top: 100 +--- + +# Download + +You can download the corresponding binary executable file for the deployment system from this page or [GitHub Release](https://github.com/OpenListTeam/OpenList/releases). + + diff --git a/pages/en/guide/installation/index.md b/pages/en/guide/installation/index.md new file mode 100644 index 000000000..e69de29bb diff --git a/pages/en/guide/installation/koyeb.md b/pages/en/guide/installation/koyeb.md new file mode 100644 index 000000000..dbda42b4a --- /dev/null +++ b/pages/en/guide/installation/koyeb.md @@ -0,0 +1,118 @@ +--- +top: 42 +categories: + - guide + - installation +--- + +# Koyeb + +::: tip +The [Koyeb free plan](https://www.koyeb.com/pricing#features) includes: + +- 1 Web Service (500 MB RAM, regions: Germany 🇩🇪 or United States 🇺🇸) +- ~~1 Postgres~~ (Koyeb's free database only provides 5 hours per month; this guide uses the [Supabase free plan](https://supabase.com/pricing) instead) + +::: + +## Deployment Overview + +- Create a database on Supabase to persist OpenList configuration data +- Deploy the OpenList application on Koyeb + +## Prerequisites + +- A [Supabase account](https://supabase.com/dashboard) (click to sign up) +- A [Koyeb account](https://app.koyeb.com/auth/signup) (click to sign up) + +### Create the Database + +Sign in to [Supabase](https://supabase.com/dashboard). + +1. Create a new project. +2. In **New project**, set the **Database password** and save it. Select **Central EU (Frankfurt)** as the region. + > Choose a region close to your Koyeb server — **Frankfurt** or **Washington D.C** is recommended. +3. Open the new project and click the **Connect** button to find the DB_HOST. + ![db_host](/img/koyeb/db_host.png) + +## Create the Service + +1. Sign in to the [Koyeb console](https://app.koyeb.com/) and click **Create App**. +2. Select **Docker** under Web service. +3. Set **Image** to `openlistteam/openlist:latest` and click **Next**. +4. Choose the **Free** instance under **CPU Eco 🌱** and click **Next**. + +### Configure Deployment + +1. Enable the **Override** toggle next to **Command**. +2. Enter `./openlist server --config /tmp/config.json` in the input box. + ::: warning + You must specify the config file path; otherwise you will get the error: the current user does not have write and/or execute permissions on the `./data` directory (`/opt/openlist/data`). + ::: + +![deployment](/img/koyeb/deployment.png) + +### Set up Environment Variables and Files + +#### Add environment variables + +| Key | Value | Purpose | +| ------------------------- | --------------------------------- | ------------------------------------------------------------------------------- | +| `TZ` | `Asia/Shanghai` (or any timezone) | Set the container timezone | +| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | +| `OPENLIST_ADMIN_PASSWORD` | Initial admin password | Strongly recommended; otherwise a random password is generated on every restart | + +![environment](/img/koyeb/environment.png) + +#### Add configuration file + +Switch to the `Files` tab, click **Add file**, and set **Path** to `/tmp/config.json` (matching the path specified in the Command). + +Enter the following in **File content**: + +```json +{ + "force": false, + "jwt_secret": "random_generated", + "database": { + "type": "postgres", + "host": "replace_with_your_DB_HOST", + "port": 5432, + "user": "postgres", + "password": "replace_with_your_database_password", + "name": "postgres", + "db_file": "", + "table_prefix": "x_", + "ssl_mode": "require", + "dsn": "" + }, + "scheme": { + "address": "0.0.0.0", + "http_port": 5244 + }, + "temp_dir": "/tmp/temp", + "bleve_dir": "/tmp/bleve", + "log": { + "enable": false + } +} +``` + +![file](/img/koyeb/file.png) + +### Configure Ports + +Change the **Port** to `5244`. + +## Deploy and Verify + +1. Review the summary and click **Save and deploy**. +2. After a short wait you should see OpenList initialization messages in the LOG panel. +3. Once deployment is complete, click the link provided by Koyeb to access OpenList. + +![success](/img/koyeb/success.png) + +## Updates and Maintenance + +- **Updates**: Go to the Service detail page. If the image is set to `latest`, simply click **Redeploy** when a new version is available. +- **Logs**: View real-time logs on the Service detail page, or click **Details** under Scaling. diff --git a/pages/en/guide/installation/manual.md b/pages/en/guide/installation/manual.md new file mode 100644 index 000000000..5c1081956 --- /dev/null +++ b/pages/en/guide/installation/manual.md @@ -0,0 +1,401 @@ +--- +top: 60 +categories: + - guide + - installation +--- + +# Manual installation + +## Get OpenList + +You can download the corresponding binary executable file for the deployment system from [Download](./download) page or [GitHub Release](https://github.com/OpenListTeam/OpenList/releases). + +[![latest version](https://img.shields.io/github/release/OpenListTeam/OpenList)](https://github.com/OpenListTeam/OpenList/releases) + +## Install using package manager + +### Linux + +Debian/Ubuntu can be installed from OpenList's APT repository and PPA repository, and the service (daemon) will be automatically configured. + +#### APT Repository + +Recommended - Automatic GPG Setup + +```bash +# One-line install with automatic GPG key setup +curl -fsSL https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/install-apt.sh | bash + +# Then install OpenList +sudo apt install openlist -y +``` + +Manual APT Setup with GPG Verification (Modern systems - Ubuntu 22.04+/Debian 12+) + +```bash +# Download and install GPG keyring +curl -fsSL https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/openlist-archive-keyring.gpg | \ + sudo tee /usr/share/keyrings/openlist-archive-keyring.gpg > /dev/null + +# Add repository with GPG verification +echo "Types: deb +URIs: https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/ +Suites: ./ +Signed-By: /usr/share/keyrings/openlist-archive-keyring.gpg" | \ + sudo tee /etc/apt/sources.list.d/openlist.sources + +# Update and install +sudo apt update && sudo apt install openlist -y +``` + +Manual APT Setup without GPG Verification (Not Recommended) + +```bash +# Modern systems (Ubuntu 22.04+/Debian 12+) +echo "Types: deb +URIs: https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/ +Suites: ./ +Trusted: yes" | sudo tee /etc/apt/sources.list.d/openlist.sources + +# Legacy systems (Ubuntu <22.04/Debian <12) +echo "deb [trusted=yes] https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/ ./" | \ + sudo tee /etc/apt/sources.list.d/openlist.list + +# Update and install +sudo apt update && sudo apt install openlist -y +``` + +#### PPA Repository + +Alternative - Launchpad + +```bash +# Add PPA repository +sudo add-apt-repository ppa:openlist/server +sudo apt update + +# Install OpenList +sudo apt install openlist -y +``` + +#### Flatpak + +OpenList can be installed as a Flatpak package on most Linux distributions. Flatpak provides a sandboxed environment and automatic updates. + +[![Flatpak package](https://img.shields.io/badge/dynamic/json?color=4A90E2&label=Flatpak&query=tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FOpenListTeam%2FOpenList-FLATPAK%2Freleases%2Flatest&logo=flatpak)](https://github.com/OpenListTeam/OpenList-FLATPAK/releases) + +One-line Installation (Recommended) + +```bash +curl -fsSL https://github.com/OpenListTeam/OpenList-FLATPAK/releases/latest/download/install-flatpak.sh | bash +``` + +Manual Installation + +```bash +# Install Flatpak (if not already installed) +# On Ubuntu/Debian +sudo apt install flatpak + +# On Fedora +sudo dnf install flatpak + +# On Arch Linux +sudo pacman -S flatpak + +# Add Flathub repository (required for dependencies) +flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + +# Download and install OpenList Flatpak package +wget https://github.com/OpenListTeam/OpenList-FLATPAK/releases/latest/download/org.oplist.openlist-linux-x86_64.flatpak +flatpak install --user --bundle org.oplist.openlist-linux-x86_64.flatpak -y + +# Start OpenList +flatpak run org.oplist.openlist server +``` + +### Windows + +OpenList can be installed from package managers Scoop main and WinGet on Windows. + +#### Scoop + +[![Scoop package](https://repology.org/badge/version-for-repo/scoop/openlist.svg)](https://repology.org/project/openlist/versions) + +```powershell +scoop install openlist +openlist server +``` + +#### WinGet + +```powershell +winget install OpenListTeam.OpenList +openlist server +``` + +### macOS + +OpenList is available via Homebrew on macOS. + +#### Homebrew + +[![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/openlist.svg)](https://repology.org/project/openlist/versions) + +```bash +$ brew install openlist +``` + +### Android + +::: tip +OpenList follows the AGPL 3.0 open-source license, assumes no responsibility for any downstream derivative projects, and reserves the right to pursue their compliance with the same license. +::: + +There are four ways to choose based on your needs + +1. Using **https://github.com/OpenListTeam/OpenList-Mobile** +2. Using **https://github.com/LeoHaoVIP/AListLiteAndroid** +3. Using **https://github.com/jing332/AListFlutter** (no longer maintained) +4. Use `termux` to run + - Reference: **https://anwen-anyi.github.io/index/14-android_install.html** + - Note: Remember to authorize the APP, set the background running and battery saving policy to unlimited, otherwise it may be killed in the background, causing it to be suddenly interrupted and unusable during background use. + +#### Termux + +[![Termux package](https://repology.org/badge/version-for-repo/termux/openlist.svg)](https://repology.org/project/openlist/versions) + +```bash +pkg update +pkg install openlist +openlist server +``` + +## Running + +```bash +Usage: + openlist [command] + +Available Commands: + admin Show admin user's info and some operations about admin user's password + cancel2fa Delete 2FA of admin user + completion Generate the autocompletion script for the specified shell + crypt Encrypt or decrypt local file or dir + help Help about any command + kill Force kill openlist server process by daemon/pid file + lang Generate language json file + restart Restart openlist server by daemon/pid file + server Start the server at the specified address + start Silent start openlist server with `--force-bin-dir` + stop Same as the kill command + storage Manage storage + version Show current version of OpenList + +Flags: + --data string data folder (default "data") + --config string config file (default "data/config.json") + --debug start with debug mode + --dev start with dev mode + --force-bin-dir Force to use the directory where the binary file is located as data directory + -h, --help help for openlist + --log-std Force to log to std + --no-prefix disable env prefix + +Use "openlist [command] --help" for more information about a command. +``` + +::: tip +If there is a prompt as follows:It is because [your GLIBC version is too low](../../faq/why#lib64-libc-so-6-version-glibc-2-28-not-found-required-by-openlist-or-accept-function-not-implemented), it is recommended to download the musl version. + +```txt +lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./openlist) +accept: function not implemented +``` + +When you see the output of `start server @ 0.0.0.0:5244` and no error is reported afterwards, it means that the operation is successful. The initial password will be output when running for the first time. The program listens to port 5244 by default. Now open `http://ip:5244` You can see the login page, please see [WebDav](../advanced/webdav) for webdav. + +**For Flatpak Users** + +If you installed OpenList via Flatpak, use the following commands instead: + +```bash +# Start server +flatpak run org.oplist.openlist server + +# Show admin info +flatpak run org.oplist.openlist admin + +# Generate random admin password +flatpak run org.oplist.openlist admin random + +# Set admin password +flatpak run org.oplist.openlist admin set NEW_PASSWORD + +# Show version +flatpak run org.oplist.openlist version +``` + +::: + +::: warning +Versions above v3.25.0 change the password to an encrypted hash value, and the password cannot be calculated directly. If the password is forgotten, it can only be re-**`randomly generated`** or **`manually set`**. +::: + +The `xxxx` refers to the names corresponding to different systems/architectures, generally Linux-x86/64 is openlist-linux-amd64. + +### Linux + +```bash +tar -zxvf openlist-xxxx.tar.gz +chmod +x openlist +./openlist server +./openlist admin +./openlist admin random +./openlist admin set NEW_PASSWORD +``` + +### macOS + +```bash +tar -zxvf openlist-xxxx.tar.gz +chmod +x openlist +./openlist server +./openlist admin +./openlist admin random +./openlist admin set NEW_PASSWORD +``` + +### Windows + +```powershell +Expand-Archive .\openlist-xxxx.zip +.\openlist.exe server +.\openlist.exe admin +.\openlist.exe admin random +.\openlist.exe admin set NEW_PASSWORD +``` + +## Daemon + +### Linux + +`vim /usr/lib/systemd/system/openlist.service` add the following content, where `path_openlist` is the path where openlist is located: + +```ini +[Unit] +Description=openlist +After=network.target +[Service] +Type=simple +WorkingDirectory=path_openlist +ExecStart=path_openlist/openlist server +Restart=on-failure +[Install] +WantedBy=multi-user.target +``` + +Then `systemctl daemon-reload`, now you can use these commands to manage the program: + +- Start: `systemctl start openlist` +- Shut down: `systemctl stop openlist` +- Self-start: `systemctl enable openlist` +- Cancel Self-start: `systemctl disable openlist` +- Status: `systemctl status openlist` +- Restart: `systemctl restart openlist` + +Can't configure daemon? [**Video Tutorial**](https://www.bilibili.com/video/BV1rF41197Qv?t=187.0) + +### macOS + +Edit `~/Library/LaunchAgents/org.openlist.plist` in any way and add the following content, modify `path_openlist` to be the path where OpenList is located, and `path/to/working/dir` to be the working path of OpenList: + +```xml + + + + + Label + org.openlist + KeepAlive + + ProcessType + Background + RunAtLoad + + WorkingDirectory + path/to/working/dir + ProgramArguments + + path_openlist/openlist + server + + + +``` + +Then, execute `launchctl load ~/Library/LaunchAgents/org.openlist.plist` to load the configuration, now you can use these commands to manage the program: + +- Start: `launchctl start ~/Library/LaunchAgents/org.openlist.plist` +- Close: `launchctl stop ~/Library/LaunchAgents/org.openlist.plist` +- Unload configuration: `launchctl unload ~/Library/LaunchAgents/org.openlist.plist` + +### Windows + +#### Method One + +1. Download the newest `nssm` from https://nssm.cc/download. +2. Unzip the archive and go to the diretory of `nssm.exe`. +3. Hold Shift and right click on the blank space, then release and press S or select "powershell here", you should now see a blue window named "Windows PowerShell". +4. Type `.\nssm.exe install openlist`. +5. Select the path of `openlist.exe` for "Path", e.g. `D:\openlist\openlist.exe`; type `server` for "Argument". +6. You can custom "Display Name", "Description" and "Startup Type" in "Details" tab. +7. Go to "I/O" tab and select a file for both "Output (stdout)" and "Output (stderr)", e.g. `D:\openlist\stdout.log`. The file itself (`stdout.log`) may not exist, but the folder (`D:\openlist`) must exist. +8. Click on "Install Service". + You can now start the service from services.msc or task manager. + +#### Method Two + +Use **`.VBS`** script to start and stop, create two scripts respectively `start.vbs` and `stop.vbs`. +Just double-click to start it in the folder at the same level as the OpenList startup program, don't worry about no response, just go to the browser to access it. + +::: info Two startup scripts +**start.vbs** + +```bash title="vbscript" +Dim ws +Set ws = Wscript.CreateObject("Wscript.Shell") +ws.run "openlist.exe server",vbhide +Wscript.quit +``` + +**stop.vbs** + +```bash title="vbscript" +Dim ws +Set ws = Wscript.CreateObject("Wscript.Shell") +ws.run "taskkill /f /im openlist.exe",0 +Wscript.quit +``` + +1. If the script will not be created, you can download it yourself: [**Script Download**](https://www.alipan.com/s/DHPMhRtKUzY/folder/63e0961eae317bd4d4d945cda69dbb00f9837fb7) +2. If the script will not be used, you can watch the video: [**reference video**](https://www.bilibili.com/video/BV1wWYTzdE4B) + How to realize Windows startup automatically, you can refer to the script mentioned above to use the video (second). + +::: + +::: info +For all platform, you can use follow command to silent start, stop and restart. (v3.4.0 and later) + +```bash +openlist start +openlist stop +openlist restart +``` + +::: + +## How to update + +Download the new version of OpenList and replace the previous one. diff --git a/pages/en/guide/installation/paas.md b/pages/en/guide/installation/paas.md new file mode 100644 index 000000000..099694f46 --- /dev/null +++ b/pages/en/guide/installation/paas.md @@ -0,0 +1,105 @@ +--- +top: 30 +categories: + - guide + - installation +--- + +# PaaS + +::: tip +The documentation may not be updated in a timely manner. Please make adjustments based on the key information and the current deployment interface of the platform. +::: + +## ClawCloud + +### One-click deployment + +::: danger +Due to the removal of `PUID`/`PGID` environment variable support for better container specifications in [OpenList v4.1.1](https://github.com/OpenListTeam/OpenList/releases/tag/v4.1.1), ClawCloud platform will not be able to function properly due to lack of permission to write data (including using images or app in App Store). + +As of October 20 2025, ClawCloud has not yet merged our Deployment Template. One-click deployment is temporarily unavailable. If you want to deploy on ClawCloud, please refer to [#1209 (comment)](https://github.com/OpenListTeam/OpenList/issues/1209#issuecomment-3243803024). +::: + +Click the button below to [deploy to Claw Cloud](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Dopenlist). It can also be found in App Launchpad. + +[![Run on CLAWCLOUD](/img/guide/installation/clawcloud-run.svg)](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Dopenlist) + +### Manual deployment + +#### Key Information + +| Name | Value | Description | +| :--------------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Application Name | openlist | Application name | +| Image | openlistteam/openlist:latest-lite | Image, please make sure to use the one with **lite**, otherwise the error `Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.` may occur | +| Replicas | 1 | Number of replicas, set to 1 | +| CPU | 0.2 | Number of CPU cores, set according to your own needs | +| Memory | 256M | Memory size, set according to your own needs | +| Container Port | 5244 | Mapped port, which is `5244` if you have not modified the startup command or configuration | +| Public Access | Y | Enable external access | +| Custom Domain | - | If you have your own domain name, set it according to the prompts; if not, keep the default. Please make sure to use **https** for the protocol | +| Local Storage | - | Persistent volume | +| -- Capacity | 1 | Capacity | +| -- Mount Path | /opt/openlist/data | Configured mapped directory, which is `/opt/openlist/data` if you have not modified the startup command or configuration | + +#### Reference Diagram + +![](/img/guide/installation/clawcloud-01.png) + + diff --git a/pages/en/guide/installation/reverse-proxy.md b/pages/en/guide/installation/reverse-proxy.md new file mode 100644 index 000000000..fc65cc71e --- /dev/null +++ b/pages/en/guide/installation/reverse-proxy.md @@ -0,0 +1,137 @@ +--- +top: 10 +categories: + - guide + - installation +--- + +# Reverse proxy + +OpenList listens to port 5244 by default. If modified, please also update the port number in the configuration below. + +If you are using **reverse proxy**, please ensure that the correct `Host` header is passed, as OpenList will generate the URL based on this information. + +If the `Host` header is unavailable, you can use the higher-priority `X-Forwarded-Host` header (non-standard header). If the issue persists, configure the [site_url](../../configuration/configuration.md#site-url). + +If you want to use a **sub folder**, you should configure the [site_url](../../configuration/configuration.md#site-url). Refer to [reverse proxy with sub folder](../../faq/howto.md#how-to-reverse-proxy-with-sub-directory). + +:::warning +If you need to proxy to a non-standard port, make sure to pass the `domain:port` information through the `Host` or `X-Forwarded-Host` header, otherwise the port will be lost in URLs! +::: + +## nginx + +https://nginx.org/en/docs/http/ngx_http_proxy_module.html + +Add in the `server` field of the website configuration file: + +```nginx +location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Range $http_range; + proxy_set_header If-Range $http_if_range; + proxy_redirect off; + proxy_pass http://127.0.0.1:5244; + proxy_http_version 1.1; + # the max size of file to upload + client_max_body_size 20000m; +} +``` + +::: warning +If you use the aaPanel, be sure to delete the following default configuration: + +```nginx +location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md +location ~ .\*\.(gif|jpg|jpeg|png|bmp|swf)$ +location ~ .\*\.(js|css)?$ +``` + +Disable Nginx caching in `/www/server/nginx/conf/proxy.conf` or the corresponding website configuration file. Otherwise, with the default configuration, when accessing large files, Nginx will attempt to cache the remote file locally first, resulting in playback failures. + +```nginx +proxy_cache cache_one; # Remove this line +proxy_max_temp_file_size 0; # Add this line +``` + +::: + +::: tip Nginx Host Variables Differences + +| Variable | Description | Includes Port | Notes | +| -------------------- | ------------------------------------------------ | ------------- | ---------------------------------------------------------------------------------------------------- | +| `$http_host` | The original `Host` request header | Yes | If the `Host` request header is missing, the value will be empty; not recommended for best practices | +| `$host` | Server name from the `Host` request header field | No | Defaults to the server's `server_name` if `Host` field is missing | +| `$host:$server_port` | Server name + port | Yes | Use this combination when you need to use a non-default port (other than 80 or 443) | + +::: + +If HTTP/3 is needed, the corresponding `Host` line should be modified to: + +```nginx +proxy_set_header Host $host:$server_port; +``` + +This modified configuration will be compatible with requests using HTTP/2 or lower versions. + +## Apache + +Add the anti-generation configuration item `ProxyPass` under the `VirtualHost` field, such as: + +```xml + + ServerName myapp.example.com + ServerAdmin webmaster@example.com + DocumentRoot /www/myapp/public + AllowEncodedSlashes NoDecode + ProxyPreserveHost On + ProxyPass "/" "http://127.0.0.1:5244/" nocanon + ProxyPassReverse "/" "http://127.0.0.1:5244/" nocanon + +``` + +## Caddy + +Add the reverse proxy configuration item `reverse_proxy` under the `Caddyfile` file, for example: + +``` +:80 { + reverse_proxy 127.0.0.1:5244 +} +``` + +If deployed on a server that is functioning properly on port 443 and accessed using a domain name, it is recommended to use this configuration to let Caddy automatically request a certificate: + +(Replace example.com with your own resolved domain name) + +``` +example.com { + reverse_proxy 127.0.0.1:5244 +} +``` + +## Tutorial: Setting up Reverse Proxy in aaPanel + +1. Login to the aaPanel and add a new website. + +:: + +![bt_new_website](/img/guide/reverse_proxy/bt_new_website.png) + +2. Modify the website settings. + +![bt_new_website_01](/img/guide/reverse_proxy/bt_new_website_01.png) + +3. Remove the default panel code. + +![bt_delete_default_config_01](/img/guide/reverse_proxy/bt_delete_default_config_01.png) +![bt_delete_default_config_02](/img/guide/reverse_proxy/bt_delete_default_config_02.png) + +4. Add the reverse proxy. + +![bt_reverse_proxy](/img/guide/reverse_proxy/bt_reverse_proxy.png) + +> If you need to apply for an `SSL certificate`, you can first apply for the certificate in the `SSL` option, and then set up the reverse proxy. Alternatively, you can set up the reverse proxy first, disable the proxy function, apply for an `SSL` certificate, and then enable the proxy again. diff --git a/pages/en/guide/installation/script.md b/pages/en/guide/installation/script.md new file mode 100644 index 000000000..1ffce149b --- /dev/null +++ b/pages/en/guide/installation/script.md @@ -0,0 +1,107 @@ +--- +top: 70 +categories: + - guide + - installation +--- + +# One-click Script + +Requirements: + +- Linux with systemd or OpenRC +- Root privileges for installation +- `curl`, `tar` installed +- Architectures listed in the [download page](download) + +## Latest + +### Install + +::: code-group + +```bash [🌍Global] +curl -fsSL https://res.oplist.org/script/v4.sh > install-openlist-v4.sh && sudo bash install-openlist-v4.sh +``` + +```bash [🇨🇳CN] +curl -fsSL https://res.oplist.org.cn/script/v4.sh > install-openlist-v4.sh && sudo bash install-openlist-v4.sh +``` + +```bash [GitHub] +curl -fsSL https://raw.githubusercontent.com/OpenListTeam/OpenList-Resource/refs/heads/main/script/v4.sh > install-openlist-v4.sh && sudo bash install-openlist-v4.sh +``` + +::: + +```bash +欢迎使用 OpenList 管理脚本 + +基础功能: +1、安装 OpenList +2、更新 OpenList +3、卸载 OpenList +------------------- +服务管理: +4、查看状态 +5、密码管理 +6、启动 OpenList +7、停止 OpenList +8、重启 OpenList +------------------- +配置管理: +9、备份配置 +10、恢复配置 +------------------- +高级选项: +11、Docker 管理 +12、定时更新 +13、系统状态 +14、关于 +------------------- +0、退出脚本 +``` + +根据界面提示,输入`1`即可安装 + +### openlist-manager + +::: tip +**It can only be used after the installation is complete.** +::: + +Use command: `openlist` or `openlist-manager` + +```bash +欢迎使用 OpenList 管理脚本 + +基础功能: +1、安装 OpenList +2、更新 OpenList +3、卸载 OpenList +------------------- +服务管理: +4、查看状态 +5、密码管理 +6、启动 OpenList +7、停止 OpenList +8、重启 OpenList +------------------- +配置管理: +9、备份配置 +10、恢复配置 +------------------- +高级选项: +11、Docker 管理 +12、定时更新 +13、系统状态 +14、关于 +------------------- +0、退出脚本 +``` + +### FAQ + +1. Q: The architecture I am using is listed as supported on the download page, why does the installation script say it is not? + + A: This is because the installation script is currently unable to recognise your CPU architecture. To help us add CPU architecture recognition to the installation script, please submit the output of the `arch` and `uname -m` commands to the issue page. diff --git a/pages/en/guide/installation/source.md b/pages/en/guide/installation/source.md new file mode 100644 index 000000000..6b3b68b71 --- /dev/null +++ b/pages/en/guide/installation/source.md @@ -0,0 +1,108 @@ +--- +top: 20 +categories: + - guide + - installation +--- + +# Run from source + +:::danger +This step is only for users who need to modify the source code by themselves. If you don't understand, please skip it. +::: + +## Environmental preparation + +First, you need to have an environment of `git`, `nodejs>=24`, `pnpm`, `golang>=1.24`, `gcc` + +On **Windows**, use Scoop: + +```powershell +scoop install git nodejs-lts pnpm go mingw-winlibs +``` + +On **Linux/macOS**, use Homebrew: + +```bash +# Debian / Ubuntu +sudo apt install git curl build-essential + +# Fedora / RH +sudo dnf install @development-tools +# sudo yum groupinstall "Development Tools" + +# Arch +sudo pacman -S git curl base-devel + +# macOS +xcode-select --install + +# Install nodejs and golang use Homebrew for newer version +curl -o- https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | bash +brew install node@24 go +corepack enable pnpm +``` + +## Building the frontend + +```bash +git clone https://github.com/OpenListTeam/OpenList-Frontend.git +cd OpenList-Frontend +pnpm install && pnpm build +``` + +## Build the backend + +Clone `https://github.com/OpenListTeam/OpenList` for this project, copy the `dist` directory of the previous step to the `public` directory under the project, and then: + +```bash +appName="openlist" +builtAt="$(date +'%F %T %z')" +goVersion=$(go version | sed 's/go version //') +gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) +gitCommit=$(git log --pretty=format:"%h" -1) +version=$(git describe --long --tags --dirty --always) +webVersion=$(curl -s --max-time 5 "https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest" -L | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/^v//') +if [ -z "$webVersion" ]; then + webVersion="0.0.0" +fi +ldflags="\ +-w -s \ +-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.BuiltAt=$builtAt' \ +-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GoVersion=$goVersion' \ +-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitAuthor=$gitAuthor' \ +-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitCommit=$gitCommit' \ +-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.Version=$version' \ +-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.WebVersion=$webVersion' \ +" +go build -ldflags="$ldflags" . +``` + +::: details compilation tutorial videos you may need +Windows version: **https://www.bilibili.com/video/BV1Xr4y1z723** (Although it is V2 version, it is the same as V3 version..) +Linux version: **https://www.bilibili.com/video/BV1GW4y1s742** +Compile documents: **https://www.yuque.com/anwenya/alist/glqlhu** +::: + +## Building within Docker + +Install Docker, clone the repository, then navigate to the root directory of the repository. No further preparation is required. + +#### Basic + +```bash +docker build -t openlistteam/openlist:beta . +``` + +#### build-arg + +```bash +docker build -t openlistteam/openlist:beta-ffmpeg --build-arg INSTALL_FFMPEG=true . +``` + +Available build args: + +| | Desc | +| :-------------------- | -------------- | +| `INSTALL_FFMPEG=true` | Install ffmpeg | +| `INSTALL_ARIA2=true` | Install aria2 | diff --git a/pages/en/guide/installation/truenas.md b/pages/en/guide/installation/truenas.md new file mode 100644 index 000000000..e4d3c636c --- /dev/null +++ b/pages/en/guide/installation/truenas.md @@ -0,0 +1,88 @@ +--- +top: 44 +categories: + - guide + - installation +--- + +# Use TrueNAS Scale + +## Install the App + +Since OpenList is not officially available in the TrueNAS Apps catalog, you must use the **Custom App** feature to install it. + +Follow these steps to deploy OpenList via the **Install iX App** wizard: + +1. **Application Name** + Enter a name for your application (e.g., `openlist`). Keep the default version unless you require a specific tag. + + ![Application Name](/img/truenas/InstallCustomAppApplicationName.png) + +2. **Image Configuration** + - **Repository**: `openlistteam/openlist` + - **Tag**: `latest` (or specify another version if needed) + Leave all other fields at their defaults. + + ![Image Configuration](/img/truenas/InstallCustomAppImageConfiguration.png) + +3. **Container Configuration** + - **Environment Variables**: + Add a new variable: + - Name: `UMASK` + - Value: `022` + - **Restart Policy**: Select `Unless Stopped` to ensure automatic restart on failure. + - **Entrypoint**: Leave unchanged — it is pre-configured. + Other settings may be adjusted as desired. + + ![Container Configuration](/img/truenas/InstallCustomAppContainerEntrypoint.png) + +4. **Device** + No device passthrough is required for basic operation. Keep defaults. + +5. **Security Context Configuration** + - ✅ Check **Custom User** + - Set **UID** and **GID** to match a non-root user with permissions to access your storage volumes. + Default: `568/568` (apps/apps) — recommended for security. + - ⚠️ Avoid using `root` (UID/GID = 0) unless absolutely necessary — it poses a security risk. + > 💡 If you plan to use an **ixVolume** later, ensure this user has write permissions to the target dataset. + + ![Security Context Configuration](/img/truenas/InstallCustomAppSecurityContextConfiguration.png) + +6. **Network Configuration** + - Add a port mapping: + - **Host Port**: Any unused port (e.g., `10544`) + - **Container Port**: `5244` + - **Host IP**: `0.0.0.0` (accessible from any network) or restrict to a local IP if preferred. + Add additional mappings only if exposing other services. + + ![Network Configuration](/img/truenas/InstallCustomAppNetworkConfiguration.png) + +7. **Portal Configuration** + - **Port**: Match the Host Port from Step 6 (e.g., `10544`) + - **Name**: Optional — set to something descriptive like “OpenList Web UI” + Once saved, a **Web UI** button will appear on the Apps page. + + ![Portal Configuration](/img/truenas/InstallCustomAppPortalConfiguration.png) + +8. **Storage Configuration** + - ✅ **Mandatory**: At least one volume must be configured. + - Click **Add** → Select storage type (`ixVolume` recommended for persistence). + - **Mount Path**: `/opt/openlist/data` + - Ensure the container’s user (from Step 5) has **write permissions** to this location. + - Optionally add more volumes for logs, configs, etc. + + > 📌 Tip: Use **ixVolume** instead of host paths when possible — it ensures better integration with TrueNAS backup and snapshot features. + + ![Storage Configuration](/img/truenas/InstallCustomAppStorageConfiguration.png) + +9. **Labels Configuration** + Skip — not required for basic operation. + +10. **Resources Configuration** _(Optional)_ + Set CPU/memory limits if you wish to constrain resource usage (e.g., 1 core, 512MB RAM). + + ![Resources Configuration](/img/truenas/InstallCustomResourcesConfiguration.png) + +11. **Final Step** + Click **Install** to deploy the OpenList container. + Wait for status to turn green → then click **Web UI** to access the interface. diff --git a/pages/en/guide/installation/worker.md b/pages/en/guide/installation/worker.md new file mode 100644 index 000000000..172341173 --- /dev/null +++ b/pages/en/guide/installation/worker.md @@ -0,0 +1,257 @@ +--- +top: 40 +categories: + - guide + - installation +--- + +# OpenList Worker + +## What is OpenList Worker + +[**OpenList Worker**](https://github.com/OpenListTeam/OpenList-Worker)(仓库 `OpenList-TSWorker`)是 OpenList 官方的 TypeScript + Serverless 移植版。后端由 Go 重写为运行于边缘平台上的 TypeScript 服务,前端复用官方 [OpenList-Frontend](https://github.com/OpenListTeam/OpenList-Frontend)(SolidJS),无需自备服务器即可一键部署。 + +- **后端**:Hono.js(TypeScript) +- **运行平台**:Cloudflare Workers / EdgeOne Functions / Alibaba Cloud ESA / Vercel / AWS Lambda +- **数据库**:Cloudflare D1(SQLite)、MySQL / MariaDB / PostgreSQL / SQL Server、Cloudflare KV +- **许可证**:AGPL-3.0 + +## Supported Platforms + +| Platform | Persistence | One-click | Notes | +| :--------------------------------- | :---------------- | :-------: | :------------------------------- | +| Cloudflare Workers | D1 / KV / JSON | ✅ | Recommended, global edge network | +| EdgeOne Makers(国际站 / 中国站) | EdgeOne Blob / KV | ✅ | Tencent Cloud edge platform | +| Alibaba Cloud ESA | EdgeKV | ✅ | Alibaba edge function | +| Vercel | In-memory(JSON) | — | No persistence by default | +| AWS Lambda(Serverless Framework) | In-memory(JSON) | — | `serverless.yml` | + +## One-click Deploy + +Click the button below to deploy to the corresponding platform: + +| EdgeOne Makers · 国际站 | EdgeOne Makers · 中国站 | Cloudflare Workers · 全球站 | +| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://console.cloud.tencent.com/edgeone/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/OpenListTeam/OpenList-Worker) | + +> If Cloudflare prompts `无法获取存储库内容`(cannot fetch repository content), fork this project first, then deploy by connecting to the GitHub repository. + +## Initialization + +::: tip +After deployment, the first visit to the site automatically enters an **installation wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. +::: + +## Environment Variables + +| Variable | Required | Default | Description | +| :----------------------------------------------------------------------- | :---------- | :--------- | :--------------------------------------------------------------------------------------------------------------- | +| `JWT_SECRET` | Recommended | — | JWT signing key, also used for data encryption and cron task auth. Auto-generated and persisted to KV when unset | +| `DB_FORMAT` | Optional | `map` | Storage format: `map` (whole JSON) / `key` (per-key) / `sql` (relational, Go-compatible) | +| `DB_DRIVER` | Optional | `auto` | Database driver: `auto` / `blob` / `cfkv` / `kv` / `d1` / `do` / `mysql` | +| `CF_ACCOUNT` | Optional | — | Cloudflare account ID (required for `cfkv` mode) | +| `CF_KV_UUID` | Optional | — | Cloudflare KV namespace ID (required for `cfkv` mode) | +| `CF_API_KEY` | Optional | — | Cloudflare API token (required for `cfkv` mode) | +| `MYSQL_URL` | Optional | — | MySQL connection string (or use `MYSQL_*` fields below) | +| `MYSQL_HOST` / `MYSQL_PORT` / `MYSQL_USER` / `MYSQL_PASS` / `MYSQL_NAME` | Optional | — | MySQL connection fields (`DB_DRIVER = mysql`) | +| `ADMIN_PASS` | Optional | — | Skip the install wizard and auto-initialize admin with this password | +| `ALLOW_URLS` | Optional | — | Comma-separated CORS allowlist | +| `MAX_UPLOAD` | Optional | `26214400` | Max size (bytes) for a whole upload (`/put`, `/form`) | +| `MAX_UPPART` | Optional | `16777216` | Max size (bytes) per chunk in multipart upload | +| `ASSET_URLS` | Optional | — | Frontend asset CDN base URL, supports the `$version` placeholder | +| `ALLOW_SEED` | Optional | — | Allowlist of hosts permitted as seed-data sources | + +## Data Backend(DB_FORMAT & DB_DRIVER) + +OpenList Worker separates persistence into two orthogonal layers: + +- **`DB_FORMAT`** — how data is serialized and stored +- **`DB_DRIVER`** — which underlying storage system is used + +### `DB_FORMAT`(data storage format) + +| Value | Description | +| :--------------- | :-------------------------------------------------------------------------- | +| `map`(default) | Whole object serialized as a single JSON value, ideal for KV / Blob storage | +| `key` | Per-key storage, one record per entity (avoids large JSON) | +| `sql` | Relational tables, fully compatible with the Go backend (for D1 / MySQL) | + +### `DB_DRIVER`(database driver) + +| Value | Description | Suitable platform | +| :---------------- | :---------------------------------------------------------------------------- | :------------------------ | +| `auto`(default) | Auto-detect available driver(priority: blob → cfkv → kv → d1 → memory) | Universal, works anywhere | +| `blob` | Tencent EdgeOne Blob / Alibaba ESA Blob | EdgeOne / ESA | +| `cfkv` | Cloudflare KV REST API(requires `CF_ACCOUNT` / `CF_KV_UUID` / `CF_API_KEY`) | External / cross-account | +| `kv` | Cloudflare KV binding | Cloudflare Workers | +| `d1` | Cloudflare D1 (SQLite) | Cloudflare Workers | +| `do` | Cloudflare Durable Objects (SQLite) | Cloudflare Workers | +| `mysql` | External MySQL | Node.js container runtime | + +### Recommended combinations + +```bash +# Cloudflare Workers + D1 (recommended, SQL format compatible with Go backend) +DB_FORMAT=sql +DB_DRIVER=d1 +``` + +> **Backward compatibility:** the legacy `DB_DRIVER=json` auto-converts to `DB_FORMAT=map` + auto-detected driver. + +#### Table naming (SQL format) + +The `sql` format uses columnar tables with the same naming strategy as the Go backend's GORM: snake*case + pluralized table names, plus the fixed `x*` prefix. + +| Go struct | Table name | +| :------------ | :---------------- | +| `SettingItem` | `x_setting_items` | +| `SharingDB` | `x_sharing_dbs` | +| `Storage` | `x_storages` | +| `User` | `x_users` | +| `Meta` | `x_metas` | +| _(TS only)_ | `x_plugins` | + +The prefix is fixed to `x_` (matching the Go backend default), so no extra configuration is needed to share the same physical database with the Go backend. + +## Deploy to Cloudflare Workers + +### Prerequisites + +- A [Cloudflare](https://dash.cloudflare.com/) account +- Node.js 18+ and pnpm + +### Option 1: One-click deploy + +Click the **Deploy to Cloudflare Workers** button above. If it prompts that the repository cannot be fetched, fork the project first and deploy via the GitHub repository connection. + +### Option 2: Manual deploy with Wrangler + +```bash +# 1. Clone and install dependencies +git clone https://github.com/OpenListTeam/OpenList-Worker.git +cd OpenList-Worker +pnpm install + +# 2. Configure wrangler.jsonc (JWT_SECRET, KV / D1 bindings) + +# 3. Deploy to Cloudflare Workers +pnpm run deploy:worker +# or: pnpm run deploy (builds frontend + deploys backend) +``` + +### D1 database + +Use Cloudflare's _Automatic resource provisioning_: omit `database_id` in `wrangler.d1.jsonc`, and wrangler (>= 4.45.0) auto-creates a D1 database with the same name and writes back the ID on deploy: + +```jsonc +{ + "vars": { "DB_FORMAT": "sql", "DB_DRIVER": "d1" }, + "d1_databases": [{ "binding": "DB", "database_name": "openlist-data-base" }], +} +``` + +After deployment, configure the KV namespace binding and environment variables in the [Worker dashboard](https://dash.cloudflare.com/). + +## Deploy to EdgeOne + +### One-click deploy + +Click the **EdgeOne** deploy button above, choose the international or China site: + +- [International console](https://console.edgeone.ai/makers) +- [China console](https://console.cloud.tencent.com/edgeone/makers) + +### Persistence + +EdgeOne Makers uses `@edgeone/pages-blob` for persistence. The default `auto` driver auto-detects Blob. You can also explicitly set `DB_DRIVER = "blob"` (with `DB_FORMAT = "map"`) or `DB_DRIVER = "kv"` (with `DB_FORMAT = "key"`). + +### Scheduled tasks + +EdgeOne supports scheduled refresh via `edgeone.json`. Set `cron_secret` in the payload to your `JWT_SECRET` value, and configure the schedule: + +```jsonc +{ + "schedules": [ + { + "name": "token-refresh", + "cron": "0 2 * * *", + "path": "/api/task/refresh", + "method": "POST", + "payload": { "cron_secret": "" }, + "timezone": "Asia/Shanghai", + }, + ], +} +``` + +## Deploy to Alibaba Cloud ESA + +OpenList Worker ships a dedicated ESA edge function entry (`esa-entry.ts`), which adapts Alibaba Cloud EdgeKV into the project's KV interface. + +### Build & deploy + +```bash +# 1. Install dependencies +pnpm install + +# 2. Build (produces dist/esa-entry.js) +pnpm run build +``` + +`esa.jsonc` defines the edge function entry, install/build commands, and static assets directory: + +```jsonc +{ + "name": "openlist", + "entry": "./dist/esa-entry.js", + "installCommand": "pnpm install --no-frozen-lockfile", + "buildCommand": "pnpm run build", + "assets": { "directory": "./dist" }, +} +``` + +### KV namespace + +Configure the EdgeKV namespace via the `KV_NAMESPACE` environment variable (default `openlist`). + +::: tip +ESA EdgeKV is eventually consistent. The entry implements a module-level TTL cache (60s) to avoid "saved settings revert after refresh" caused by cross-node sync delay. +::: + +## Local Development + +```bash +# 1. Install backend dependencies +pnpm install + +# 2. Fetch frontend and run the backend (unified dev server) +pnpm run dev:unified + +# Or run the worker directly (frontend built separately) +pnpm run dev:worker +``` + +## Production Deploy + +```bash +# One-click deploy (frontend build + backend deploy to Cloudflare Workers) +pnpm run deploy +``` + +## Troubleshooting + +::: details Cloudflare prompts "cannot fetch repository content" +Fork the project first, then deploy by connecting to the GitHub repository instead of the direct one-click URL. +::: + +::: details Settings revert after save (ESA / EdgeOne) +This is usually a KV/CDN cache consistency issue. The entry already forces `no-cache` on `/api/*` GET responses and implements a module-level KV cache. If it persists, check that your KV namespace is correctly bound and not read-only. +::: + +::: details How do I reset the admin password? +The admin password is set during the install wizard. To reset, you can set `ADMIN_PASS` temporarily and redeploy, or clear the persisted config and re-run the wizard. +::: + +## Repository + +- [OpenListTeam/OpenList-Worker](https://github.com/OpenListTeam/OpenList-Worker) diff --git a/pages/en/guide/migrate.md b/pages/en/guide/migrate.md new file mode 100644 index 000000000..13f860ea6 --- /dev/null +++ b/pages/en/guide/migrate.md @@ -0,0 +1,113 @@ +--- +categories: + - guide +top: 99999 +--- + +# Migrate from AList V3 + +::: danger +Smooth migration is not compatible with Alist v3.46 and higher versions. If you want to migrate, please avoid upgrading to a higher version. +::: + +## 1. Revoke Authorization for Alist API + +As the API service provided by the original author may have been taken over by a third party, there is a risk of information leakage or account bans. If you have concerns, it is recommended to revoke authorization or log in again to ensure your security. + +Since it's not possible to provide detailed steps for every platform, we invite helpful users to visit [https://github.com/OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) and share screenshots of the authorization revocation process. + +The following content is provided by helpful online users in [OpenListTeam/OpenList-Docs#5](https://github.com/OpenListTeam/OpenList-Docs/issues/5) and [OpenListTeam/OpenList-Docs#6](https://github.com/OpenListTeam/OpenList-Docs/issues/6). We thank them for their contributions!🙏✨💖 + +::: info The following methods only apply to cloud drives mounted using officially supported Open API methods. +::: + +::: tip Quick overview of how to revoke authorization for each cloud drive ♿️ + +- **Baidu Netdisk App** - My - Settings - Account Management - Authorization Management - Alist - Revoke Authorization +- **Aliyun Drive** - My - Upper right gear icon - Privacy Settings - Authorization Management - Alist - Revoke Authorization +- **115 APP** - Life (scroll down) - Account & Security - Multi-device Login Management - Third-party Login +- **China Unicom Cloud** - Check login account on webpage - Future recommendation: follow tutorial for packet capture login +- **Baidu Photo** - Avatar - App Settings - Account Management - Authorization Management +- **JianGuoYun** - Upper left three lines - Settings - Third-party App Management - Revoke Authorization +- **OneDrive** - https://account.live.com/consent/Manage - Revoke Authorization + +::: + +### Aliyun Drive + +1. Log in to Aliyun Drive +2. Visit the link: [https://www.alipan.com/o/oauth/auth-list](https://www.alipan.com/o/oauth/auth-list) + ![](/img/guide/migrate/aliyun_remove1.png) +3. Find Alist under “**Authorized Cloud Services**,” click to enter, then click "**Revoke Authorization**" + ![](/img/guide/migrate/aliyun_remove2.png) +4. Successfully revoked + ![](/img/guide/migrate/aliyun_remove3.png) + +### Aliyun Drive APP + +![](/img/guide/migrate/aliyun_remove4.jpg) + +### Baidu Netdisk + +1. Log in to Baidu Netdisk +2. Visit the link: [https://passport.baidu.com/v6/appAuthority](https://passport.baidu.com/v6/appAuthority) + ![](/img/guide/migrate/baidu_remove1.png) +3. Find Alist in Authorization Management, click to enter, then click "**Revoke Authorization**" + ![](/img/guide/migrate/baidu_remove2.png) +4. Successfully revoked + +### Baidu Netdisk APP + +![](/img/guide/migrate/baidu_remove3.jpg) + +### OneDrive Business + +Link: https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade/quickStartType~/null/sourceType/Microsoft_AAD_IAM?Microsoft_AAD_IAM_legacyAADRedirect=true + +![](/img/guide/migrate/odb_remove1.jpg) + +### OneDrive Personal + +Link: https://account.live.com/consent/Manage + +![](/img/guide/migrate/odp_remove1.jpg) + +### Google Drive + +Link: https://console.cloud.google.com + +![](/img/guide/migrate/google_remove1.png) + +![](/img/guide/migrate/google_remove2.png) + +![](/img/guide/migrate/google_remove3.png) + +## 2. Backup Configuration Files + +Use the [Backup & Restore](/en/guide/advanced/backup) function to back up the configuration files to your local device. + +Additionally, you will need to back up the `data` folder from Alist V3, which contains site configuration files and the database. + +## 3. Uninstall Alist V3 + +Uninstall according to the method you used for installation. + +## 4. Install OpenList + +Follow the instructions provided in the documentation to install OpenList. + +::: danger +If you are using Docker for deployment, make sure to modify the Volume mapping by changing the configuration file path from `/opt/alist/data` to `/opt/openlist/data`. Otherwise, your configuration files will be lost after updating the version and rebuilding the container! + +In addition, it is recommended to delete the previous container and then recreate it to avoid failure to run due to environmental changes. +::: + +## 5. Restore Configuration Files + +If your Alist V3 version is below v3.46, you can migrate directly—keeping the previous `data` folder and only replacing the OpenList binary files. + +Otherwise, use the [Backup & Restore](/en/guide/advanced/backup) function to restore the backed-up configuration files to OpenList. + +## 6. Reset Settings + +Once in OpenList’s admin panel, on each settings page, click the `Load Default Settings` button at the bottom, then click `Save`. diff --git a/pages/en/index.md b/pages/en/index.md new file mode 100644 index 000000000..86fef0239 --- /dev/null +++ b/pages/en/index.md @@ -0,0 +1,30 @@ +--- +layout: home +title: OpenList +titleTemplate: OpenList Docs +description: OpenList is a resilient, community-driven file list program that supports multiple cloud storage platforms. Built as a secure fork of AList with enhanced security and long-term governance. +comment: false + +hero: + name: OpenList + tagline: 🗂️ A file list program that supports multiple storage, powered by Gin and SolidJS + image: + src: /logo.svg + alt: OpenList + actions: + - theme: brand + text: Get Started + link: /en/guide/ + - theme: alt + text: View GitHub + link: https://github.com/OpenListTeam/OpenList + - theme: alt + text: Disclaimer + link: /en/terms#disclaimer + - theme: alt + text: Terms of Service + link: /en/terms + - theme: alt + text: Privacy Policy + link: /en/privacy +--- diff --git a/pages/en/privacy.md b/pages/en/privacy.md new file mode 100644 index 000000000..db229faa3 --- /dev/null +++ b/pages/en/privacy.md @@ -0,0 +1,21 @@ +--- +sidebar: false +aside: false +outline: false +pageClass: statement-page +comment: false +contributors: false +--- + +# Privacy Policy + +We highly value your privacy. We **DO NOT collect any of your personal information**. However, please be aware that your IP address and other metadata might be collected by your ISP or intermediate network nodes beyond our control. + +## Privacy Policy + +- We DO NOT collect, store, process, or analyze any of your personal data. +- We DO NOT use any tracking technologies (such as cookies, fingerprinting, web beacons, etc.) to gather your data. +- We DO NOT share your information with any third parties. +- Your actions within this service are NOT recorded by us. +- HOWEVER, your IP address and visit time may be collected by your ISP or transit nodes as part of normal Internet infrastructure behavior, which is BEYOND our control. +- If you have any privacy-related concerns, please feel free to contact us. diff --git a/pages/en/seeds/background.md b/pages/en/seeds/background.md new file mode 100644 index 000000000..e49bce51a --- /dev/null +++ b/pages/en/seeds/background.md @@ -0,0 +1,45 @@ +--- +categories: + - seeds +top: 990 +--- + +# Background + +## Overview + +1. Users can generate a seed for one or more files — on local disk or on a drive — to share file information, or to re-upload after the files are lost. +2. Users can share seeds to perform offline rapid upload, file sharing, backup, and space release across drives, users, and devices. +3. Generic BT clients can download the files inside a seed through a server proxy over the drive; OSS-compatible clients can rapid-upload or download via the server proxy. + +## Glossary + +| Term | Meaning | +| --------------- | ---------------------------------------------------------------------------------------------------- | +| Transfer Seed | A portable file-metadata system built on three sidecar formats | +| Sidecar | A companion file alongside the main file, e.g. `movie.mp4.torrent` | +| Seed | An `openlist-sharing-seed` v1 document describing path, size, hashes, and sources for a set of files | +| Hash Matrix | Specifies which hashes to compute per file (md5/sha1/sha256 × whole/pieces) | +| Piece | A block produced by splitting a file at `piece_size`; each piece has its own hash | +| Whole hash | A single hash computed over the entire file | +| Piece hashes | An array of hashes, one per piece | +| Rapid upload | Reusing known hashes so a drive completes instantly without re-sending content | +| Relay / Transit | Save synchronously to an intermediate drive, then copy server-side to the final drive | +| Channel | Where a seed is mounted on a drive (`driver` + `mount_path`) | + +## Format roles + +| Format | Suffix | Encoding | Completeness | Primary use | +| --------------------- | ---------- | ----------- | --------------------------------------------------------- | -------------------------------------------- | +| OpenList Sharing Seed | `.oss` | JSON | Highest, losslessly carries all fields | OpenList-internal share / edit / recalculate | +| BitTorrent | `.torrent` | bencode | Medium, standard fields + `x-openlist` lossless extension | Generic BT clients / offline tools | +| CAS | `.cas` | Base64 JSON | Low, only rapid-upload hashes | 189pc rapid upload | + +The three are projections of the same file metadata: `.oss` is the superset, while `.torrent` and `.cas` are its semantic projections (see [Design principles](/en/seeds/design)). + +## Typical scenarios + +1. **Cross-drive migration**: move files from Aliyundrive to 189pc. A seed carrying SHA1 + MD5 + piece hashes lets the destination hit the hash and rapid-upload without a full download-then-upload. +2. **Sharing**: share a `.oss` or `.torrent` so a friend rapid-uploads or offline-downloads on their own drive without consuming the sharer's egress. +3. **Backup and space release**: generate a seed, delete the original, and re-rapid-upload it later from the seed. +4. **Integrity verification**: per-piece hashes verify whether a downloaded file is complete and locate damaged pieces. diff --git a/pages/en/seeds/design.md b/pages/en/seeds/design.md new file mode 100644 index 000000000..755a7bb1b --- /dev/null +++ b/pages/en/seeds/design.md @@ -0,0 +1,287 @@ +--- +categories: + - seeds +top: 980 +--- + +# Design principles + +## Goals + +1. **Portable**: three convertible sidecar formats for sharing file information across drives, users, and devices. +2. **Reuse hashes to avoid redundant downloads**: reuse drive-provided hashes or stream-hash files. +3. **Third-party compatible**: `.torrent` follows BT v1, and the `.cas` single-file five fields are byte-compatible with the reference project. +4. **Safe and bounded**: parsing is bounded by byte/file/depth/path-traversal limits, and seeds never serialize credentials. + +## Format relationships + +The three formats are different projections of the same file metadata. `.oss` is the most complete; `.torrent` and `.cas` are its semantic projections: + +- `.oss` ⊇ `.torrent`: `.oss` converts to `.torrent` when it has the required BT info (SHA-1 whole + pieces); `.torrent` always converts to `.oss`. +- `.oss` ⊇ `.cas`: `.oss` converts to `.cas` when it has whole + piece MD5; `.cas` always converts to `.oss`. +- Conversion diagnostics report exactly what is missing. + +```mermaid +graph TD + OSS[.oss most complete] -->|has BT-required info| TORRENT[.torrent] + TORRENT -->|always convertible| OSS + OSS -->|has whole + piece MD5| CAS[.cas] + CAS -->|always convertible| OSS + TORRENT -.->|has x-cas extension| CAS + CAS -.->|missing SHA1 pieces, not directly convertible| TORRENT +``` + +## Seed design + +### `.oss` structure + +`.oss` is the most complete JSON container: + +| Field | Description | +| --------------------------- | ----------------------------------------------------------------- | +| `format` / `version` | fixed `openlist-sharing-seed` / `1` | +| `name` | seed name (editable, auto-derived if empty) | +| `comment` | overall comment | +| `created_at` / `created_by` | creation time / creator | +| `piece_size` | piece size (default 10 MiB) | +| `trackers` | tracker list | +| `channels` | channels (`driver` + optional `mount_path`), public metadata only | +| `files` | file array | + +Each file (`files[]`) carries: + +| Field | Description | +| ----------------------------------- | ----------------------------------------------- | +| `path` / `size` / `modified` | path / size / mtime | +| `comment` | per-file comment | +| `hashes.md5` / `sha1` / `sha256` | whole-file hashes | +| `hashes.pieces` | per-algorithm piece hashes | +| `sources` | public direct (`/d/`) or share (`/sd/`) sources | +| `cas_slice_md5` / `cas_create_time` | CAS aggregate slice MD5 / create time | +| `missing_channels` | drives that failed rapid upload | + +### `.torrent` structure + +A standard BitTorrent v1 file whose info dict holds `piece length` and `pieces` (SHA-1), plus two extension keys: + +- `x-openlist`: the full OpenList seed (MD5/SHA256, comments, channels, shares, etc.). +- `x-cas`: 189pc CAS info (`file_md5` / `slice_md5` / `slice_size`). + +### `.cas` structure + +A base64-encoded JSON, compatible with the reference five-field single-file payload and extended for multiple files: + +```json +{ + "name": "example", + "size": 12345, + "md5": "d41d8cd98f00b204e9800998ecf8427e", + "sliceMd5": "…", + "create_time": "1720000000", + "slice_md5s": ["…", "…"], + "slice_size": 10485760, + "files": [{ "name": "a.txt", "size": 100, "md5": "…", "sliceMd5": "…", "create_time": "…" }] +} +``` + +- Single file uses the top-level five fields (byte-compatible with the reference project); multiple files use the `files` array. +- `slice_md5s` / `slice_size` are optional extensions preserving the per-piece MD5 list for 189pc rapid upload. +- Third-party CAS clients use standard JSON parsing and ignore extra fields, remaining backward compatible. + +## Field spec and safety bounds + +Every inbound seed (regardless of whether it comes from `.oss` / `.torrent` / `.cas` or arbitrary JSON) first passes through **normalization**, which enforces these hard limits at parse time: + +| Constraint | Value | +| ------------------------- | ------------------------------------------------------------------ | +| `piece_size` valid range | 16 KiB ~ 64 MiB (default 10 MiB) | +| File count | 1 ~ 100,000 | +| Max per-file path length | 4096 bytes | +| Max path depth | 64 levels | +| Piece hash count | must equal `ceil(size / piece_size)` (0 for empty files) | +| Max bencode nesting depth | 32 | +| Max bencode item count | 100,000 | +| Path safety | reject `\0`, backslash `\`, leading `/`, empty segments, `.`, `..` | +| Name safety | reject empty, `.`, `..`, or names containing `/` or `\` | +| `sources` URL | must be absolute with a scheme | +| `channels.mount_path` | reject `?`, `#`, `\0` | + +Seeds **never serialize credentials** (e.g. `cookie`, `access_token`); `channels` keeps only public metadata like `driver` and `mount_path`. Normalization also applies these fixes: + +- Hashes are lowercased; invalid hashes error (md5 32, sha1 40, sha256 64 hex chars). +- Missing `name` falls back to the first segment of `files[0].path`. +- Missing `format` / `version` default to `openlist-sharing-seed` / `1`. +- Missing `created_at` / `created_by` default to now / `OpenList`. +- Duplicate file paths error. + +## Hash matrix design + +The hash matrix is a 3×2 boolean selection specifying which hashes to compute per file: + +```json +{ + "md5": { "whole": true, "pieces": true }, + "sha1": { "whole": true, "pieces": true }, + "sha256": { "whole": true, "pieces": true } +} +``` + +- **whole**: a single whole-file hash (`hashes.md5` / `sha1` / `sha256`). +- **pieces**: per-piece hash arrays (`hashes.pieces.md5[]` / `sha1[]` / `sha256[]`). + +Normalization rules: + +1. **All-empty means all-on**: when all six flags are `false`, they are treated as all `true` ("compute as much as possible"). +2. **Format forcing**: selecting `.torrent` forces `sha1.whole = true` and `sha1.pieces = true`; selecting `.cas` forces `md5.whole = true` and `md5.pieces = true`. The UI disables these checkboxes with a "required by this format" hint. +3. **Reuse drive hashes**: the generation wizard calls `capabilities` first and pre-selects the hashes the drive already exposes, avoiding redundant downloads. + +The matrix decides whether generation must download: piece hashes are generally not exposed by drive listings, so any `pieces` requirement forces a streaming download; a missing whole hash also forces a download. + +## Encoding and conversion details + +### bencode (the encoding underneath `.torrent`) + +`.torrent` uses bencode. Dictionary keys must be sorted by byte order. The encoder handles the four value types: + +- integer → `ie` (errors when out of safe-integer range) +- byte string → `:` +- list → `le` +- dictionary → `de` + +The parser enforces depth ≤ 32, items ≤ 100,000, and rejects trailing data. + +### The `.torrent` info dictionary + +- Single file: `info` holds `name`, `piece length`, `pieces`, `length`, optional `md5sum`. +- Multi-file: `info` holds `name`, `piece length`, `pieces`, `files` (each with `length`, `path`, optional `md5sum`). +- `pieces` is the concatenation of all SHA-1 piece hashes in order (20 bytes each); parsing verifies the piece count equals `ceil(total_size / piece_size)`. +- `info_hash` = SHA-1(bencode(info)), used for magnet links and BT identification. + +OpenList extension keys: + +- `x-openlist`: a lossless bencode-embedding of the full `.oss` seed (JSON values recursively mapped to bencode values), letting OpenList clients fully reconstruct the metadata. +- `x-cas`: `{ cloud, file_md5, slice_md5, slice_md5s, slice_size }` for 189pc rapid upload; written only for a single file with MD5. +- `announce` / `announce-list`: derived from `trackers` (first tracker becomes `announce`). +- `creation date` / `comment` / `created by`: standard metadata. + +When `x-openlist` is present at parse time, its consistency with the info dict is validated (name, piece_size, file count, each file's path and size); any mismatch errors out to prevent forgery. + +### Multi-file `.torrent` boundary limit + +All files in a BT v1 torrent share one piece sequence, so when `files.length > 1`, every file except the last must have a size divisible by `piece_size`; otherwise a piece is split by a file boundary and cannot be legally divided. Conversion reports `a file boundary splits a piece`. + +### `.cas` `sliceMd5` rules + +- When the piece size equals 10 MiB (`DEFAULT_PIECE_SIZE`) and a per-piece MD5 list exists: + - one piece → `sliceMd5 = that piece MD5`. + - multiple pieces → `sliceMd5 = md5(per-piece MD5s uppercased and joined by newline)`. +- When per-piece MD5 is absent: + - file ≤ 10 MiB → use the whole-file MD5 as `sliceMd5`. + - file > 10 MiB and no legacy `cas_slice_md5` → conversion errors (cannot derive safely). + +Encoding `.cas` emits base64-encoded JSON; decoding tries base64 first and falls back to plain JSON (for legacy files). + +### Format auto-detection + +The `parse` API detects the format by the first character: `{` → `.oss` (JSON), `d` → `.torrent` (bencode dict), otherwise → `.cas`. An explicit `format` hint can override this. + +## Architecture + +Seed capability is provided by two equivalent implementations, one per backend: + +| Backend | Language | Format library | API layer | +| ----------------- | ---------- | ------------------------------------------------------------------------- | --------------------------- | +| OpenList-Backends | Go | `pkg/torrent/` (bencode, hash writer, generate, parse, convert, diagnose) | `server/handles/torrent.go` | +| OpenList-TSWorker | TypeScript | `internal/seed/` (`codec.ts`, `hash.ts`, `types.ts`) | `server/seed.ts` | + +Both share the same field spec and codec logic (bencode sorting, CAS `sliceMd5` derivation, hash-matrix normalization, consistency validation), so seeds produced by the Cloudflare Worker and Go versions interoperate. + +```mermaid +graph LR + UI[HopeUI frontend] -->|POST /fs/seed/*| GO[Go backend] + UI -->|POST /fs/seed/*| TS[TS Worker backend] + GO --> FMT[pkg/torrent format library] + TS --> TSFMT[internal/seed codec] + GO --> DRV[drivers: 189pc / PutURL / offline] + TS --> TSDRV[drivers: rapid / session / stream] +``` + +Core components: + +- **`HashWriter` / `TorrentPieceHasher`**: streaming hashers that maintain whole-file hashes (md5/sha1/sha256) plus per-piece hashes in a single pass, avoiding a second read. +- **Normalization layer**: `normalizeSeed` (and the Go equivalent) applies field defaults and safety validation. +- **Conversion layer**: encode/decode between `.oss` ↔ `.torrent` ↔ `.cas`, with missing-information diagnostics. +- **Capability preflight**: `capabilities` summarizes per file "hashes available / download needed / streamable", reused by the UI and generation logic. + +## Data model + +Both the Go and TS backends share one data model with identical field names, ensuring seeds from either backend interoperate. The core structures are: + +### `Seed` + +| Field | Type | Description | +| ------------ | ------------------- | ----------------------------------- | +| `format` | `string` | fixed `openlist-sharing-seed` | +| `version` | `number` | fixed `1` | +| `name` | `string` | seed name | +| `comment` | `string` | overall comment | +| `created_at` | `string` (ISO 8601) | creation time | +| `created_by` | `string` | creator | +| `piece_size` | `number` | piece size in bytes, default 10 MiB | +| `trackers` | `string[]` | tracker list | +| `channels` | `SeedChannel[]` | channels that saved successfully | +| `files` | `SeedFile[]` | file array | + +### `SeedFile` + +| Field | Type | Description | +| ------------------ | -------------- | -------------------------------- | +| `path` | `string` | relative path | +| `size` | `number` | size in bytes | +| `modified` | `string` | mtime (ISO 8601) | +| `comment` | `string` | per-file comment | +| `hashes` | `SeedHashes` | hash collection | +| `sources` | `SeedSource[]` | public direct / share sources | +| `cas_slice_md5` | `string` | CAS aggregate slice MD5 (legacy) | +| `cas_create_time` | `string` | CAS creation time (legacy) | +| `missing_channels` | `string[]` | drives that failed rapid upload | + +### `SeedHashes` + +| Field | Type | Description | +| -------- | -------- | ---------------------------------------------- | +| `md5` | `string` | whole-file MD5 | +| `sha1` | `string` | whole-file SHA-1 | +| `sha256` | `string` | whole-file SHA-256 | +| `pieces` | `object` | `{ md5[], sha1[], sha256[] }` per-piece arrays | + +### `SeedSource` + +| Field | Type | Description | +| ------------ | -------- | --------------------------------------------------- | +| `type` | `string` | `openlist-direct` or `openlist-share` | +| `url` | `string` | absolute URL, restricted to the configured site URL | +| `expires_at` | `string` | expiry time (optional) | +| `share_id` | `string` | share ID (for `openlist-share`) | + +### `SeedChannel` + +| Field | Type | Description | +| ------------ | -------- | --------------------- | +| `driver` | `string` | driver name | +| `mount_path` | `string` | mount path (optional) | + +### `CASPayload` (`.cas` payload) + +| Field | Type | Description | +| ------------- | ----------- | -------------------------------------------------- | +| `name` | `string` | file name | +| `size` | `number` | file size | +| `md5` | `string` | whole MD5 | +| `sliceMd5` | `string` | aggregate slice MD5 | +| `create_time` | `string` | creation time | +| `slice_md5s` | `string[]` | per-piece MD5 (optional extension) | +| `slice_size` | `number` | piece size (optional extension, default 10 MiB) | +| `files` | `CASFile[]` | per-file array for multi-file (optional extension) | + +> Single file uses the top-level five fields (`name`/`size`/`md5`/`sliceMd5`/`create_time`), byte-compatible with the reference project; multiple files use the `files` array; `slice_md5s`/`slice_size` preserve per-piece MD5 for 189pc rapid upload. diff --git a/pages/en/seeds/faq.md b/pages/en/seeds/faq.md new file mode 100644 index 000000000..caf74d9c4 --- /dev/null +++ b/pages/en/seeds/faq.md @@ -0,0 +1,65 @@ +--- +categories: + - seeds +top: 940 +--- + +# FAQ + +## Notes + +- `.cas` per-piece MD5 and multi-file support rely on optional extension fields; the single-file five fields remain byte-compatible with the reference project. +- Relayed transfer requires an intermediate storage with synchronous native reuse or PutURL. +- Recalculation requires the source file to already exist on the server. +- The generated `.torrent` is a valid BT file, but OpenList itself is not a BT peer. +- All seed parsing is bounded by byte, file-count, depth, and path-traversal limits. + +## Common questions + +### Why is my generated `.cas` flagged as Legacy CAS? + +A legacy CAS only carries the five fields `name`, `size`, `md5`, aggregate `sliceMd5`, and creation time, with no per-piece MD5 list, so conversion is lossy. Newer generation writes `slice_md5s` / `slice_size` to preserve per-piece MD5, and the preview no longer shows the Legacy CAS warning when piece hashes are present. + +### Why can't a seed with only MD5 convert to `.torrent`? + +`.torrent` follows the BT v1 spec and requires SHA-1 whole + piece hashes. MD5 alone cannot produce a valid `pieces` field; conversion reports the missing SHA-1 pieces explicitly. + +### Why does multi-file `.torrent` conversion report "a file boundary splits a piece"? + +All files in a BT v1 torrent share one piece sequence. In multi-file mode, every file except the last must have a size divisible by `piece_size`; otherwise a piece is cut by a file boundary and cannot be legally divided. Adjust the piece size or use a single file so sizes align. + +### Why does rapid upload report `unavailable`? + +When the destination drive cannot reuse the hashes in the current seed, the operation reports `unavailable` instead of silently degrading to a normal upload. Make sure the seed contains the hashes the target drive requires (see the [rapid upload matrix](/en/seeds/rapid-upload)). + +### Why can't relayed transfer use asynchronous offline download? + +Asynchronous offline download cannot guarantee the intermediate storage has fully landed before the server-side copy to the final drive. Relaying therefore requires the intermediate storage to support synchronous native reuse or PutURL. + +### Can a generated `.torrent` be downloaded by a generic BT client? + +The generated `.torrent` is a valid BT v1 file and can be handed to an offline tool via magnet. However, OpenList itself is not a BT peer; whether generic BT clients can download depends on the tracker/webseed deployment. + +### Why does recalculation require the source file on the server? + +Recalculation re-reads an existing server-side file to recompute hashes; it never downloads from an external source. Upload or save the file to the server first if it is not already there. + +### How do I enable automatic sidecar generation on upload? + +The upload API is controlled by headers: set `X-Seed-Sidecars` (e.g. `oss,torrent,cas`) to specify formats directly, or combine `X-Generate-Seed: on` with the storage-level `seed_policy` and the global `seed_auto_generate_policy`. The global policy defaults to `off`, so nothing is generated by default. + +### Why can't asynchronous upload (`as_task=true`) generate sidecars? + +Sidecar generation must read the full upload stream and hash it in one pass. An async task cannot guarantee the complete stream is available in the same process and written synchronously, so the upload API returns 400 for the "sidecars + async upload" combination. + +### What does an all-empty hash matrix mean? + +When all six flags (md5/sha1/sha256 × whole/pieces) are `false`, the system treats it as "compute as much as possible", equivalent to selecting everything. To produce a minimal seed, explicitly select only the hashes you need. + +### What is `x-openlist` inside a `.torrent`? + +`x-openlist` is OpenList's lossless extension key that bencode-embeds the full `.oss` seed into a standard torrent. OpenList clients use it to fully reconstruct MD5/SHA256, comments, channels, and shares; parsing also validates its consistency with the info dictionary to prevent forgery. + +### Why are background seed-generation tasks lost on restart? + +Requests over 1 GiB are queued as background tasks, but those tasks are not persisted; an unfinished task is lost when the service restarts and must be re-triggered. Small files (≤ 1 GiB) are generated synchronously and are unaffected. diff --git a/pages/en/seeds/generate.md b/pages/en/seeds/generate.md new file mode 100644 index 000000000..20a9e7a0e --- /dev/null +++ b/pages/en/seeds/generate.md @@ -0,0 +1,101 @@ +--- +categories: + - seeds +top: 970 +--- + +# Generating seeds + +## Generate on upload + +While uploading, you can enable `.oss` / `.torrent` / `.cas` sidecars (all off by default). The sidecars are produced alongside the streaming hash calculation without a second read. + +## Generate from the context menu + +Right-click one or more files and choose "Generate transfer seed" to open the wizard: + +1. **Seed name**: editable, auto-derived when empty (file name for single selection, common prefix or folder for multiple). +2. **Formats**: enable `.oss` / `.torrent` / `.cas` (any combination). +3. **Hash matrix**: choose MD5 / SHA-1 / SHA-256 whole and piece hashes, pre-selecting the hashes the drive already provides. +4. **Piece size**: defaults to 10 MiB. +5. **File list**: shows each file's size, drive-provided hashes, whether a download is needed, and the generation mode (direct/download); per-file comments and per-file share/direct toggles. +6. **Trackers**: picked from the system-configured tracker list. + +## Seed naming + +The seed name is editable; when empty it is auto-derived in this priority: + +1. **Single file**: the file name with its extension removed. +2. **Multiple files sharing a base name**: if all files share the same name after removing extensions (e.g. `a.mkv`, `a.srt`, `a.ass` in one folder), use that common name. +3. **Otherwise**: the common parent directory name. +4. **Fallback**: `OpenList Seed`. + +Naming is subject to the same safety checks (rejecting empty names, `.`, `..`, or names containing `/` or `\`). + +## Generation modes + +- **Direct**: the drive already provides all required hashes, no download needed. +- **Download**: files are streamed and hashed on the fly, with estimated traffic shown. +- If the drive cannot stream files, generation is blocked with a hint; requests over 1 GiB are queued as a background task. + +## Generate API + +Right-click generation calls `POST /fs/seed/generate` (compatible with the legacy `POST /fs/torrent/generate`). Request body: + +| Field | Type | Description | +| --------------- | ---------------------- | ------------------------------------------------------- | +| `paths` | `string[]` | source file/directory paths | +| `formats` | `SeedFormat[]` | target formats: `oss` / `torrent` / `cas` | +| `hash_matrix` | `SeedHashMatrix` | hash matrix (see [Design principles](/en/seeds/design)) | +| `piece_size` | `number` | piece size in bytes, default 10 MiB | +| `name` | `string` | seed name (optional, auto-derived) | +| `comment` | `string` | overall comment (optional) | +| `file_comments` | `Record` | per-file comments (optional) | +| `trackers` | `string[]` | tracker list (optional) | +| `share_files` | `string[]` | file paths to record share links (`/sd/`) | +| `direct_files` | `string[]` | file paths to record public direct links (`/d/`) | +| `output_path` | `string` | directory to save the generated sidecars | + +Returns `SeedGenerateResult`: on synchronous completion it carries the produced paths; a non-empty `task` or `async` field means the request was queued as a background task. + +## Upload sidecar mechanics + +The upload API (`POST /fs/put`) hashes **while streaming** and then writes the sidecars alongside the main file. Whether to generate is decided in this priority order: + +1. Header `X-Seed-Sidecars`: explicit formats (e.g. `oss,torrent,cas`); non-empty enables generation. +2. Header `X-Generate-Seed`: `on` / `true` / `1` enables, `off` disables, `inherit` or empty falls through. +3. Storage-level `seed_policy`: the target storage's `on` / `off` / `inherit`. +4. Global setting `seed_auto_generate_policy`: defaults to `off`. + +Other sidecar headers: + +| Header | Purpose | +| -------------------- | ------------------------------------------------------------ | +| `X-Seed-Format` | Equivalent to `X-Seed-Sidecars`, specifies formats | +| `X-Seed-Piece-Size` | Piece size in bytes; forced to 10 MiB when `cas` is included | +| `X-Seed-Hash-Matrix` | JSON hash matrix controlling which hashes are written | + +Constraints and details: + +- Upload sidecars require **synchronous upload**; an async upload (`as_task=true`) returns 400 (sidecars need a complete streaming hash, which cannot be guaranteed in a background task). +- Hashing reuses the upload stream via `io.TeeReader`, so one read both uploads and hashes — no second read. +- After upload, the byte count read by the `HashWriter` is verified against the declared size; a mismatch errors out (keeping sidecar hashes consistent with the landed file). +- Sidecars are written as `.` in the same directory (e.g. `movie.mp4.torrent`, `movie.mp4.cas`, `movie.mp4.oss`). +- When no format is given, enabled formats are resolved from the global `seed_format_policies`. + +## Background tasks + +When the total size of a generation request exceeds 1 GiB, it is queued as a background task so the request thread is not blocked. On completion the sidecars are written to `output_path`, and progress/result can be checked in the task center. + +Requests over the 1 GiB threshold go background automatically; everything else returns the produced paths synchronously. + +## Previewing seeds + +Opening a `.oss` / `.torrent` / `.cas` file shows a unified preview with: + +- file list and sizes +- per-file hashes (whole + pieces, copyable with a per-piece popup) +- file comments, timestamps, multi-line overall comment +- trackers, channels, and share/direct sources +- conversion feasibility (✓/✗, hover or click for missing reasons) +- available operations (rapid upload, offline download, transfer, convert, edit, recalculate, preview/remove a single file) diff --git a/pages/en/seeds/index.md b/pages/en/seeds/index.md new file mode 100644 index 000000000..e281156b0 --- /dev/null +++ b/pages/en/seeds/index.md @@ -0,0 +1,26 @@ +--- +categories: + - seeds +top: 1000 +--- + +# Transfer Seeds + +## What are Transfer Seeds + +Transfer Seeds are a portable file-metadata system built on three sidecar formats for sharing file information, offline rapid-upload, backup, and space release across drives, users, and devices. + +- **`.oss`** (`openlist-sharing-seed` v1): the most complete JSON format; every field is optional. +- **`.torrent`**: a standard BitTorrent v1 file with OpenList extensions. +- **`.cas`**: a content-addressable payload compatible with the reference project [OpenList-CAS](https://github.com/GitYuA/OpenList-CAS), used for 189pc rapid upload. + +The three formats convert between each other (when enough information is present) and support generation, preview, rapid upload, offline download, relayed save, editing, and recalculation. + +## Sections + +- [Background](/en/seeds/background) — why transfer seeds exist and current capability boundaries +- [Design principles](/en/seeds/design) — design goals and the structure of the three formats +- [Generating seeds](/en/seeds/generate) — generate on upload / from the context menu / preview +- [Using seeds](/en/seeds/usage) — rapid upload, offline download, relay, convert, edit, recalculate +- [Rapid upload matrix](/en/seeds/rapid-upload) — hashes each drive exposes and requires +- [FAQ](/en/seeds/faq) — notes and common questions diff --git a/pages/en/seeds/rapid-upload.md b/pages/en/seeds/rapid-upload.md new file mode 100644 index 000000000..c34bbe56a --- /dev/null +++ b/pages/en/seeds/rapid-upload.md @@ -0,0 +1,75 @@ +--- +categories: + - seeds +top: 950 +--- + +# Rapid upload matrix + +The seed system relies on drive-provided hashes to avoid redundant downloads. Different drives differ in the hashes they "provide in listing" and "require for rapid upload". The two dimensions are merged below into a single two-dimensional matrix: **rows = hashes provided by the source listing, columns = hashes required by the target for rapid upload**; each cell answers "can this source rapid-upload to this target". + +## Legend + +| Symbol | Meaning | +| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ✅ | Direct rapid upload: the source listing already provides every hash the target needs, no content download | +| 🔶 | Needs hashing: the source provides part of the required hashes but is missing piece hashes or another algorithm, so the file must be downloaded to compute the rest first | +| ❌ | Cannot rapid upload: the source lacks the key hash the target requires | +| ➖ | Not applicable: the target drive does not support rapid upload (normal upload only) | + +## Rapid upload matrix + +| Source provides \ Target needs | MD5 | MD5 + pieces | SHA1 | MD5 + SHA1 | SHA1 or MD5 | GCID | SHA256 | no rapid upload | +| ------------------------------ | --- | ------------ | ---- | ---------- | ----------- | ---- | ------ | --------------- | +| **MD5** | ✅ | 🔶 | ❌ | 🔶 | ✅ | ❌ | ❌ | ➖ | +| **SHA1** | ❌ | ❌ | ✅ | 🔶 | ✅ | ❌ | ❌ | ➖ | +| **MD5 + SHA1** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ❌ | ➖ | +| **MD5 + SHA1 + SHA256** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ✅ | ➖ | +| **GCID** (block SHA1) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ➖ | +| **none** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ➖ | + +## Drive hash capability mapping + +The table maps each drive to the matrix's rows (listing provides) and columns (rapid upload needs), so any "source → target" pair can be located in the matrix. + +| Drive | Hashes provided (source / row) | Rapid upload requires (target / column) | +| ------------------ | -------------------------------- | --------------------------------------- | +| 189pc | MD5 | MD5 + piece MD5 (`slice_md5`) | +| 189 (legacy) | MD5 | MD5 + piece MD5 | +| 189tv | MD5 | MD5 | +| Aliyundrive | SHA1 | SHA1 (`pre_hash` + `content_hash`) | +| Aliyundrive Open | SHA1 | SHA1 (`pre_hash`) | +| Baidu | none (returned MD5 is untrusted) | MD5 (`content-md5` + `slice-md5`) | +| PikPak | GCID (block SHA1) | GCID | +| 115 | SHA1 | SHA1 | +| 115 Open | SHA1 | SHA1 | +| Thunder | GCID | GCID | +| Thunder X | GCID | GCID | +| Thunder Browser | GCID | GCID | +| Quark Open | SHA1 | MD5 + SHA1 | +| Quark UC | none | MD5 + SHA1 | +| febbox | GCID | GCID | +| 139 (China Mobile) | none (`digest` not exposed) | SHA256 (rapid upload) | +| 123 | MD5 (`etag`) | MD5 | +| 123 Open | SHA1 / MD5 | SHA1 (`sha1_reuse`) or MD5 | +| Google Drive | MD5 + SHA1 + SHA256 | no rapid upload (normal upload) | +| OneDrive | none | no rapid upload (normal upload) | + +## How to read the matrix + +- **Aliyundrive → 115**: source provides SHA1, target needs SHA1 → matrix `SHA1 × SHA1` = ✅ direct rapid upload. +- **123 → 189pc**: source provides MD5, target needs MD5 + pieces → matrix `MD5 × MD5+pieces` = 🔶 must download to compute piece MD5. +- **PikPak → Aliyundrive**: source provides GCID, target needs SHA1 → matrix `GCID × SHA1` = ❌ cannot rapid upload. +- **Any source → Google Drive / OneDrive**: target has no rapid upload → last column = ➖, normal upload only. + +## Relation to the hash matrix + +The hash matrix is preflighted against these capabilities: if the source drive already provides every hash the target needs, no download is required; otherwise files are streamed and hashed on the fly. Selecting `.torrent` forces SHA-1 whole + pieces; selecting `.cas` forces MD5 whole + pieces (fixed 10 MiB) for 189pc rapid upload. + +## Notes + +- **GCID** is a block SHA1 (40 hex chars), used by PikPak / Thunder family / febbox. +- 189 and Baidu chunked uploads additionally need per-piece MD5 (`slice_md5`), so their column is `MD5 + pieces`; a source providing only MD5 is marked 🔶 (must download to compute pieces). +- The Aliyundrive family uses `pre_hash` (SHA1 of the first 1024 bytes) to trigger rapid upload, then computes the full SHA1 on hit. +- 139 rapid upload uses SHA256; 123 Open supports either SHA1 reuse (`sha1_reuse`) or MD5. +- **Note**: the matrix reflects each drive's native rapid-upload capability. Currently only 189pc CAS rapid upload is wired into the seed "rapid save" flow; other drives' rapid upload takes effect in the normal upload path (`Put` / `PutRapid`). diff --git a/pages/en/seeds/usage.md b/pages/en/seeds/usage.md new file mode 100644 index 000000000..773aa5bdd --- /dev/null +++ b/pages/en/seeds/usage.md @@ -0,0 +1,116 @@ +--- +categories: + - seeds +top: 960 +--- + +# Using seeds + +## Seed API overview + +Seed capability is exposed under `POST /fs/seed/*` (the legacy `/fs/torrent/*` routes remain compatible): + +| API | Purpose | +| ------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `parse` | Parse a seed (auto-detects `.oss` / `.torrent` / `.cas`) | +| `upload_parse` | Upload a seed file and parse it | +| `generate` | Generate a seed (see [Generating seeds](/en/seeds/generate)) | +| `convert` | Convert between formats | +| `diagnose` | Diagnose missing information for conversion | +| `capabilities` | Preflight: per-file hashes available / download needed / streamable; or probe a destination storage's save methods | +| `rapid_upload` | Rapid-upload save | +| `offline_download` | Offline download / relayed transfer | +| `update` | Edit / recalculate / remove files | +| `update_channels` | Update channels (write `channels` / `missing_channels` on success/failure) | +| `quick_save` | Quick save (alias of `rapid_upload` / `offline_download`) | + +All seed payloads are base64-encoded in the request body. Common `SeedOperationRequest` fields: + +| Field | Description | +| ---------------------- | ------------------------------------------------------------------------------- | +| `seed_data` | base64 seed content (aliases `content` / `data` / `torrent_data` also accepted) | +| `file_name` | original seed file name | +| `path` / `target_path` | destination directory | +| `selected_files` | indices into the seed's `files[]` | +| `update_channel` | whether to update channels on success | +| `remove_files` | file paths to remove | +| `recalc_files` | files to recalculate (`path` + `source_path`) | +| `hash_matrix` | hash matrix for recalculation | + +## Rapid upload + +Select files and choose a destination drive/path. The UI shows the destination drive's supported rapid-upload methods (e.g. 189pc CAS) and whether rapid upload is currently possible. Native rapid upload is preferred; if the drive cannot reuse hashes, the operation reports `unavailable` instead of silently degrading. + +Per-file save decision (`method` returned by `capabilities`): + +| Method | Meaning | Badge | +| ------------------- | ------------------------------------------------------------------------------------------ | ------ | +| `189pc_cas` | destination is 189pc and the seed has the MD5 + piece MD5 CAS needs, so rapid upload works | green | +| `put_url` | the seed has a usable direct link, so PutURL pulls server-side | green | +| `offline_download` | falls back to an offline download tool | blue | +| `download_required` | hashes cannot be reused; the server streams and re-uploads | yellow | +| `unavailable` | the destination drive supports no usable method | red | + +Only `189pc_cas` and `put_url` are true "rapid upload" (no content download). + +## Offline download + +When the seed contains usable direct/share sources, files can be downloaded into the destination drive (PutURL / offline tool / server streaming). Torrent seeds can be handed to an offline tool via magnet. + +- `.torrent`: converted to magnet as a whole (`announce` → `tr`, `name` → `dn`, `length` → `xl`) and handed to the offline tool. +- Other seeds: downloaded per file from `sources` (`/d/` direct or `/sd/` share links). + +## Relayed transfer + +Save synchronously into an intermediate drive (native rapid upload or PutURL), then copy server-side to the final drive. Asynchronous offline downloads cannot be relayed. + +Set `transit_path` (the intermediate path) with `options: { mode: "transfer" }`; the system saves synchronously to the intermediate storage, then copies to the final `path`. + +## Update channel on success + +With "update channel on success" enabled, a successful save appends the current drive to the seed's `channels`, while a failure is recorded in `missing_channels`. + +- `channels`: an array of `{ driver, mount_path }` marking drives where the seed has landed successfully. +- `missing_channels`: an array of drive names that failed rapid upload, avoiding repeat attempts. + +## Convert + +Convert between formats; missing information (e.g. no SHA-1 pieces) is reported before conversion. + +- The target format is given by `format` / `to_format` / `target_format`. +- `diagnose` lists the hashes required but missing for each target format, driving the ✓/✗ UI and its reasons. +- Feasibility mirrors generation: `.torrent` needs SHA-1 whole + pieces; `.cas` needs MD5 whole + pieces (or a whole-file MD5 for files ≤ 10 MiB). + +## Edit and recalculate + +- **Edit**: update overall comment, trackers, channels, per-file comments, and share sources; share validity is checked during editing (invalid shares report their IDs). +- **Recalculate**: pick a source directory and recompute hashes (with a selectable hash matrix). The source path is the source directory plus the seed's relative path; recalculation only reads existing server-side files. +- **Remove files**: drop specified files from the seed via `remove_files`. + +## File-operation sidecar follow + +When copying / moving / renaming / deleting a main file, enable "Follow transfer seed sidecars" to move the companion `.oss` / `.torrent` / `.cas` / `.cas.torrent` along, keeping the main file and sidecars consistent. + +| Operation | API | `follow_seed` behavior | +| --------- | ----------------- | ---------------------------------------------- | +| Copy | `POST /fs/copy` | synchronously copy sidecars to the destination | +| Move | `POST /fs/move` | synchronously move sidecars to the destination | +| Rename | `POST /fs/rename` | rename sidecars to `newName + original suffix` | +| Remove | `POST /fs/remove` | synchronously delete sidecars | + +- Sidecar path rule: main path + `.oss` / `.torrent` / `.cas` / `.cas.torrent`. +- Files only (directories are skipped); missing sidecars are silently ignored. + +## Settings + +| Setting | Description | Default | +| ---------------------------- | ------------------------------------------- | -------------------------- | +| `seed_site_url` | Public site URL for share/direct sources | empty | +| `seed_default_matrix` | Default right-click hash matrix | md5/sha1/sha256 whole=true | +| `seed_default_trackers` | Tracker list offered at generation | empty | +| `seed_default_format` | Default seed format | oss | +| `seed_format_policies` | Per-format auto-generation switches | all off | +| `seed_auto_generate_policy` | Global auto-generation policy | off | +| `seed_single_direct_preview` | Direct preview for single-file seeds | false | +| `seed_cas_direct_access` | Rapid-upload and preview CAS on open | false | +| per-storage `seed_policy` | Storage-level `inherit`/`on`/`off` override | inherit | diff --git a/snippets/reverse-tip.md b/pages/en/snippets/reverse-tip.md similarity index 54% rename from snippets/reverse-tip.md rename to pages/en/snippets/reverse-tip.md index 3ca27c7a1..59b8d25a6 100644 --- a/snippets/reverse-tip.md +++ b/pages/en/snippets/reverse-tip.md @@ -1,12 +1,5 @@ -::: en ::: danger Please read the following notes carefully This interface is a reverse-engineered interface based on legacy products. The project team will not actively maintain it. Please do not submit any issues regarding fixes or further reverse engineering requests for this driver. ::: -::: zh-CN -::: danger 请仔细阅读注意事项 - -本接口是基于历史产物的逆向接口,项目组不会主动进行维护,请勿针对此驱动提出有关于任何修复或进行进一步逆向行为需求的issue。 - -::: diff --git a/snippets/tos-tip.md b/pages/en/snippets/tos-tip.md similarity index 58% rename from snippets/tos-tip.md rename to pages/en/snippets/tos-tip.md index 9575112a7..2d534cb2d 100644 --- a/snippets/tos-tip.md +++ b/pages/en/snippets/tos-tip.md @@ -1,12 +1,5 @@ -::: en ::: danger Please use your account in a standardized manner. Do not use it for distribution services including but not limited to **`multi-person sharing`**, **`image hosting, software hosting`** and **`video external links to video websites for playback`**, as well as some other irregular uses not mentioned above. You will be responsible for the consequences of your account being blocked. ::: -::: zh-CN -::: danger - -请规范使用帐号,包含但不限于 **`多人共享使用`**、**`图床、软件床`** 和 **`视频外链到视频网站播放`** 等分发服务以及一些没有提到的不规范使用的方式导致帐号被封禁的,后果自负! - -::: diff --git a/pages/en/terms.md b/pages/en/terms.md new file mode 100644 index 000000000..d06434b7c --- /dev/null +++ b/pages/en/terms.md @@ -0,0 +1,38 @@ +--- +sidebar: false +aside: false +outline: false +pageClass: statement-page +comment: false +contributors: false +--- + +# Terms of Service + +## AGPL License Notice + +This software is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html). You are free to use, modify, and distribute the software, but must comply with AGPL-3.0 terms, including disclosing source code when distributing or offering as a service. See the link above for details. + +## Terms of Use + +- This service is for lawful use only. Users must not engage in any illegal activity through this service. +- Users assume all risks and responsibilities resulting from the use of this service. +- We reserve the right to modify, suspend, or terminate the service at any time without prior notice. +- The service is provided "as is" without any express or implied warranties of availability, accuracy, or fitness for a particular purpose. +- All downstream projects based on OpenList must strictly comply with the full terms of the AGPL 3.0 license, including proper attribution, maintaining open-source status, and adopting the same license. +- The OpenListTeam reserves the right to require downstream projects to be distributed under the same license and to pursue any violations of the license terms. +- If you have any questions, please contact us. + +## DISCLAIMER + +- THE OPENLIST PROJECT IS FREE AND OPEN-SOURCE SOFTWARE, FORKED FROM THE UPSTREAM ALIST PROJECT AND MAINTAINED BY THE OPENLISTTEAM. +- THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +- THIS SOFTWARE IS LICENSED UNDER THE [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html). YOU ARE FREE TO USE, MODIFY, AND DISTRIBUTE THE SOFTWARE, BUT MUST COMPLY WITH AGPL-3.0 TERMS, INCLUDING DISCLOSING SOURCE CODE WHEN DISTRIBUTING OR OFFERING AS A SERVICE. SEE THE LINK ABOVE FOR DETAILS. +- PLEASE COMPLY WITH RELEVANT LAWS AND REGULATIONS WHEN USING THIS SOFTWARE, AND REFRAIN FROM ANY FORM OF ABUSE. +- THIS SOFTWARE IS IMPLEMENTED BASED ON OFFICIAL SDKS OR APIS, WITHOUT MODIFYING, DAMAGING, OR INTERFERING WITH THEIR BEHAVIOR. +- IT ONLY PERFORMS HTTP 302 REDIRECTS OR TRAFFIC FORWARDING, WITHOUT INTERCEPTING, STORING, OR TAMPERING WITH ANY USER DATA. +- THIS PROJECT IS NOT AFFILIATED WITH ANY OFFICIAL PLATFORM OR SERVICE PROVIDER. +- THE MAINTAINERS ARE NOT RESPONSIBLE FOR ANY DIRECT OR INDIRECT LOSSES RESULTING FROM THE USE OR INABILITY TO USE THIS SOFTWARE. +- YOU ASSUME ALL RISKS ASSOCIATED WITH USING THIS SOFTWARE, INCLUDING BUT NOT LIMITED TO ACCOUNT BANS, DOWNLOAD SPEED LIMITS, ETC. +- ANY FORKS, BRANCHES, OR DERIVATIVE PROJECTS BASED ON OPENLIST ARE INDEPENDENT PROJECTS WITH THEIR OWN CREATORS, MAINTAINERS, AND FEEDBACK CHANNELS. THE OPENLISTTEAM DOES NOT PROVIDE ANY SUPPORT FOR THEM. +- ANY ACTIONS, CONTENT, AND CONSEQUENCES ARISING FROM SUCH FORKS, BRANCHES, OR DERIVATIVE PROJECTS ARE THE SOLE RESPONSIBILITY OF THEIR RESPECTIVE MAINTAINERS, AND THE OPENLISTTEAM IS NOT LIABLE FOR ANY SUCH ISSUES. diff --git a/pages/faq/error.md b/pages/faq/error.md index 68cb4b679..c0cca19cc 100644 --- a/pages/faq/error.md +++ b/pages/faq/error.md @@ -1,181 +1,79 @@ --- -# This is the title of the article -title: - en: OpenList Error-Code - zh-CN: OpenList 错误码 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 1 -# A page can have multiple categories categories: - faq -# A page can have multiple tags --- -::: en -This article will collect some error codes that may occur during the use of OpenList and provide corresponding solutions (including network issues, changes in cloud storage APIs, and other cases that may require manual intervention). -::: -::: zh-CN +# OpenList 错误码 + 本文将收集在使用 OpenList 过程中出现的一些错误码,并提供相应的解决方案(包括网络问题、网盘 API 更改及其他可能需要人工干预的情况)。 -::: --- -::: en -**Q**:Ali cloud disk open appears **TooManyRequests** 、**Too Many Requests** - -**A**:[**Click me to view detailed description**](../guide/drivers/aliyundrive_open.md) -::: -::: zh-CN **Q**:阿里云盘open出现 **TooManyRequests** 、**Too Many Requests** **A**:[**点我查看详细说明**](../guide/drivers/aliyundrive_open.md) -::: --- -::: en -**Q**:Ali cloud disk open appears **ExceedCapacityForbidden** - -**A**:[**Click me to view detailed description**](../guide/drivers/aliyundrive_open.md#4-other-instructions) -::: -::: zh-CN **Q**:阿里云盘open出现 **ExceedCapacityForbidden** **A**:[**点我查看详细说明**](../guide/drivers/aliyundrive_open.md#4-其他说明) -::: --- -::: en -**Q**:Token is expired(**Appears when logging in to the OpenList account**) - -**A**:It means that your `OpenList` login is valid for `48` hours by default, you can modify the configuration file `config.json` - -- If you are prompted to log in successfully when you log in and then this prompt is displayed again, check whether you have used CDN acceleration to cache the OpenList. - -::: -::: zh-CN **Q**:Token is expired(**出现在登录OpenList账号时**) **A**:是指你`OpenList`登录有效期到了默认是`48`小时,可以在修改`config.json`配置文件中 - 如果你登录的时候提示登录成功然后又显示这个提示,检查你是否使用了CDN加速等给OpenList进行了缓存导致的 -::: - --- -::: en -**Q**:Failed init storage but storage is already created: failed init storage: failed to refresh token: The input parameter refresh_token is missing. Please refer to document. - -**A**:Generally, the refresh token (token) is wrong when adding `driver`, and it can be solved by replacing it with the correct one. -::: -::: zh-CN **Q**:Failed init storage but storage is already created: failed init storage: failed to refresh token: The input parameter refresh_token is missing. Please refer to document. **A**:一般是在添加`驱动`的时候刷新令牌(token)不对,更换正确的替换了即可解决。 -::: --- -::: en -**Q**:failed get objs: failed to list objs: ForbiddenDriveNotValid:not valid driveld - -**A**:Generally, it means that `driver` has been deprecated. For example, Aliyun disk can be replaced with [**Alibaba cloud disk open**](../guide/drivers/aliyundrive_open.md). Others are temporarily unknown. -::: -::: zh-CN **Q**:failed get objs: failed to list objs: ForbiddenDriveNotValid:not valid driveld **A**:一般指的是`驱动`已经弃用,例如阿里云盘,更换成[**阿里云盘open**](../guide/drivers/aliyundrive_open.md)即可,其他的暂时未知。 -::: --- -::: en -**Q**:no such host、TLS handshake timeout、read: connection reset by peer、dns lookup failed、connect: connection refused、Client.Timeout exceeded while awaiting headers、network is unreachable - -**A**:These problems are generally caused by network problems, and you can troubleshoot and solve them yourself. - -- If you encounter it when you add `Aliyun disk open`:TLS handshake timeout ([Click me to see how to solve](./why.md#prompt-when-adding-aliyun-drive-shared-prompt-post-https-auth-aliyundrive-com-v2-account-token-net-http-tls-handshake-timeout)) - -::: -::: zh-CN **Q**:no such host、TLS handshake timeout、read: connection reset by peer、dns lookup failed、connect: connection refused、Client.Timeout exceeded while awaiting headers、network is unreachable **A**:遇到这些问题一般都是网络问题导致的,自行排查然后解决。 - 如果是添加`阿里云盘open`时候遇到:TLS handshake timeout ([点击我查看如何解决](./why.md#添加阿里云盘-分享-时提示-提示post-https-auth-aliyundrive-com-v2-account-token-net-http-tls-handshake-timeout)) -::: - --- -::: en -**Q**:Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path (**appears when mounting the driver**) - -**A**:The path to mount to, it is unique and cannot be repeated -::: -::: zh-CN **Q**:Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path (**出现在挂载驱动时**) **A**:要挂载到的路径,它是唯一的,不能重复 -::: --- -::: en -**Q**:Key: 'Storage.MountPath' Error:Field validation for 'MountPath' failed on the 'required' tag (**appears when mounting the driver**) - -**A**:The mount path is a required option, please fill in it -::: -::: zh-CN **Q**:Key: 'Storage.MountPath' Error:Field validation for 'MountPath' failed on the 'required' tag(**出现在挂载驱动时**) **A**:挂载路径是必填选项,填写一下 -::: --- -::: en -**Q**:UNIQUE constraint failed: x_meta.path (appears when adding meta information) - -**A**:When adding meta information, there can only be one path, and it cannot be repeated -::: -::: zh-CN **Q**:UNIQUE constraint failed: x_meta.path(**出现在添加元信息时**) **A**:添加元信息时路径只能有一个,不可以重复 -::: --- -::: en -**Q**:Key: 'Meta.Path' Error:Field validation for 'Path' failed on the 'required' tag (appears when adding meta information) - -**A**:When adding metadata, the path must be filled in -::: -::: zh-CN **Q**:Key: 'Meta.Path' Error:Field validation for 'Path' failed on the 'required' tag(**出现在添加元信息时**) **A**:添加元信息时,路径是必须要填写的 -::: --- -::: en -**Q**:failed get objs: failed to list objs: Sorry, sharing is not available in the current region(**PikPak/share**) - -**Q**:failed get objs: failed to list objs: terabox is not yet available in this are(**Terabox**) - -**A**:Domestic access is not supported, if you build it locally, you can check this [**Reference Solution**](https://anwen-anyi.github.io/index/07-wenti.html#_41-alist如何-使用-吃到-代理-proxy) - -- For example, Google, Mega, Terabox, etc. that require a proxy to access can be used in this way - -::: - -::: zh-CN **Q**:failed get objs: failed to list objs: Sorry, sharing is not available in the current region(**PikPak/分享**) **Q**:failed get objs: failed to list objs: terabox is not yet available in this are(**Terabox**) @@ -184,141 +82,68 @@ This article will collect some error codes that may occur during the use of Open - 例如 Google、Mega、Terabox 等这些需要代理才能访问的都可以通过这样的方法使用 -::: - --- -::: en -**Q**:Search not available(**appears when indexing**) - -**A**:The `Search Index` option is not selected, and cannot be built and used. I don’t know which search index to choose? [**Click me to view**](../guide/advanced/search.md#difference-between-different-search-indexes) -::: -::: zh-CN **Q**:Search not available(**出现在构建索引时**) **A**:`搜索索引`选项没有选择,无法构建使用,不知道选择哪个搜索索引好?[**点我查看**](../guide/advanced/search.md#不同搜索索引之间的差异) -::: --- -::: en -**Q**:only chinese and english, numbers and underscores are supported, and the length is no more than 50 (**Appears when the baidu.photo folder is renamed**) - -**A**:When renaming the baidu.photo folder, the maximum length is 50 -::: -::: zh-CN **Q**:only chinese and english, numbers and underscores are supported, and the length is no more than 50(**出现在一刻相册文件改名时**) **A**:一刻相册文件夹改名时最大50长度 -::: --- -::: en -**Q**:failed get objs: failed to list objs: NotFound.FileId:The resource file_id cannot be found. file_id:634e704cefa78f92fefd4c779f7422d820082d041(**Add Alibaba cloud disk open**) - -**A**:When adding the open storage of Alibaba Cloud disk, `root folder ID` is wrong, which of the last ID above is the wrong ID, just get the correct replacement. -::: -::: zh-CN **Q**:failed get objs: failed to list objs: NotFound.FileId:The resource file_id cannot be found. file_id:634e704cefa78f92fefd4c779f7422d820082d041(**添加阿里云盘open**) **A**:添加阿里云盘open存储时,`根文件夹ID`错误上述最后哪个ID就是错误的ID去获取正确的替换即可 -::: --- -::: en -**Q**:System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat - -**A**:Your Tampermonkey answering plug-in conflicts, just close it [**For details, click to view**](https://github.com/alist-org/alist/discussions/2399) -::: -::: zh-CN **Q**:System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat **A**:你的油猴答题插件冲突了,关闭了即可[**详情查看点击查看**](https://github.com/alist-org/alist/discussions/2399) -::: --- -::: en -**Q**:Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. - -**A**:If you enter the wrong password for 6 consecutive logins, it will be locked, and you can reset it by restarting OpenList. -::: -::: zh-CN **Q**:Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. **A**:连续登录输入6次密码错误就会锁定,重启OpenList即可重置。 -::: --- -::: en -**Q**:Failed get storage: please add a storage first. (**When adding offline download files**) - -**A**:When adding an offline download file, you need to enter which cloud disk you want to download the offline download file to and then click on the `folder` instead of adding it on the home page [**Complete Instructions**](../guide/advanced/offline-download.md) -::: -::: zh-CN **Q**:Failed get storage: please add a storage first. (**添加离线下载内容时**) **A**:添加离线下载文件时,你需要进入你想把离线下载的文件下载到哪个云盘然后就进入哪个`文件夹`,而不是在首页添加 [**完整使用说明**](../guide/advanced/offline-download.md) -::: --- -::: en -**Q**:failed get objs: failed to list objs: Unable to retrieve user's mysite URL(**When adding onedrive_app**) - -**A**:The newly created `OneDrive` user account does not take effect in real time, Delay takes effect, wait for a few hours and try again [**Case**](https://github.com/alist-org/docs/discussions/189#discussioncomment-5928892) -::: -::: zh-CN **Q**:failed get objs: failed to list objs: Unable to retrieve user's mysite URL(**添加onedrive_app时**) **A**:新建的 `OneDrive`用户账号不是实时生效,会延时生效等待几小时后试试看 [**案例**](https://github.com/alist-org/docs/discussions/189#discussioncomment-5928892) -::: --- -::: en -**Q**:failed to start: listen tcp 0.0.0.0:5244: bind: address already in use (**When starting the OpenList program**) - -**A**:Port number 5244 is already in use, check whether it is occupied (generally you have started an OpenList with port 5244), or modify the port number started by OpenList, [**How to modify**](../configuration/configuration.md#scheme) -::: -::: zh-CN **Q**:failed to start: listen tcp 0.0.0.0:5244: bind: address already in use (**启动OpenList程序时**) **A**:5244端口号已经被使用,排查是否被占用(一般来说你已经启动了一个5244端口的OpenList导致的),或者修改OpenList启动的端口号,[**如何修改**](../configuration/configuration.md#scheme) -::: --- -::: en -**Q**:**[When OpenList upload file](why.md#why-do-i-get-413-http-code-when-i-upload-a-file)**:Request failed with status code 413 - -**A**:Limit the size of the files configured nginx, modify the nginx's `client_max_body_size`,If you are a pagoda to go to the pagoda page to modify [Example](https://blog.csdn.net/u012514495/article/details/127981183) -::: -::: zh-CN **Q**:**[OpenList上传文件时提示](why.md#为什么我在上传文件时得到-http-413-错误)**:Request failed with status code 413 **A**:Nginx配置的文件大小所限制,修改Nginx的`client_max_body_size`就可以,如果你是宝塔搭建的去宝塔页面修改[示例](https://blog.csdn.net/u012514495/article/details/127981183) -::: --- -::: en -**Q**:**failed get objs: failed to list objs: query fail [61008]** - -**A**:It may be because some drivers do not support modifying the file sorting. Try to cancel the file sorting. -::: -::: zh-CN **Q**:**failed get objs: failed to list objs: query fail [61008]** **A**:可能是因为某些驱动不支持修改文件排序导致的,取消文件排序试试看 -::: --- -::: zh-CN **Q**:Docker 运行时查看日志,出现: FATA[2025-08-12 02:48:46] failed to create config file: open /opt/openlist/data/config.json: permission denied 。 **A**:挂载的目录与运行 Docker 的用户权限不一致导致的。解决方法: @@ -339,28 +164,3 @@ docker run -d --name openlist --user 1000:1000 -v ${yourDataDir}:/opt/openlist/d sudo chown -R 1001:1001 ${yourDataDir} docker run -d --name openlist -v ${yourDataDir}:/opt/openlist/data -p 5244:5244 openlistteam/openlist ``` - -::: -::: en -**Q**:When running `docker logs openlist`, the error appears: FATA[2025-08-12 02:48:46] failed to create config file: open /opt/openlist/data/config.json: permission denied 。 - -**A**:This is caused by the directory mounted not matching the user permissions running the docker. The solution: - -1. Use the `--user` parameter to specify the user running the container, assuming the user is `1000:1000`. - - First, ensure that the `${yourDataDir}` directory has permissions of `1000:1000`. If not, use `sudo chown -R 1000:1000 ${yourDataDir}` to change the directory permissions. - - Then run the Docker with the following command: - -```bash -docker run -d --name openlist --user 1000:1000 -v ${yourDataDir}:/opt/openlist/data -p 5244:5244 openlistteam/openlist -``` - -2. Change the owner of the `${yourDataDir}` directory to `1001:1001`, and then run the Docker: - -```bash -sudo chown -R 1001:1001 ${yourDataDir} -docker run -d --name openlist -v ${yourDataDir}:/opt/openlist/data -p 5244:5244 openlistteam/openlist -``` - -::: diff --git a/pages/faq/howto.md b/pages/faq/howto.md index 569aae1ee..d64705eb9 100644 --- a/pages/faq/howto.md +++ b/pages/faq/howto.md @@ -1,57 +1,17 @@ --- -# This is the title of the article -title: - en: How to - zh-CN: 怎么做 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 1 -# A page can have multiple categories categories: - faq -# A page can have multiple tags --- -## How to add password for a file/folder? { lang="en" } +# 怎么做 -## 如何为文件/文件夹添加密码? { lang="zh-CN" } +## 如何为文件/文件夹添加密码? -:::en -Add a [meta](../guide/advanced/meta.md) record. -::: -:::zh-CN 添加[元信息](../guide/advanced/meta.md) -::: - -## How to reverse proxy with sub directory? { lang="en" } -## 如何对子目录进行反向代理? { lang="zh-CN" } +## 如何对子目录进行反向代理? -:::en -An example of using nginx to reverse proxy to https://example.com/openlist: - -- Normal installation -- Set [site_url](../configuration/configuration.md#site-url) to `https://example.com/openlist` or just `/openlist` then restart openlist -- Add a reverse proxy record in nginx - -```nginx -location /openlist/ { - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Range $http_range; - proxy_set_header If-Range $http_if_range; - proxy_redirect off; - proxy_pass http://127.0.0.1:5244/openlist/; - # the max size of file to upload - client_max_body_size 20000m; -} -``` - -::: -:::zh-CN 使用 nginx 反向代理到 https://example.com/openlist 的示例: - 正常安装 @@ -73,24 +33,8 @@ location /openlist/ { } ``` -::: - -## How to get password if i forget it? { lang="en" } - -## 忘记密码怎么办? { lang="zh-CN" } - -:::en -If the password is forgotten, it can only be re-**`randomly generated`** or **`manually set`** +## 忘记密码怎么办? -```bash -# Randomly generate a password -./openlist admin random -# Manually set a password, `NEW_PASSWORD` refers to the password you need to set -./openlist admin set NEW_PASSWORD -``` - -::: -:::zh-CN 如果忘记了密码只能通过重新 **`随机生成`** 或者 **`手动设置`** ```bash @@ -100,122 +44,42 @@ If the password is forgotten, it can only be re-**`randomly generated`** or **`m ./openlist admin set NEW_PASSWORD ``` -::: - -## How to modify the listening port { lang="en" } +## 如何修改监听端口 -## 如何修改监听端口 { lang="zh-CN" } - -:::en -Refer to [config](../configuration/configuration.md#scheme) -::: -:::zh-CN 参考[config](../configuration/configuration.md#scheme) -::: - -## How to upgrade { lang="en" } - -## 如何更新 { lang="zh-CN" } -:::en -Except for the incompatible version marked in the changelog, you can directly replace the binary file to upgrade. +## 如何更新 -For Docker user, just remove the old container and pull the new image then run it. -::: -:::zh-CN 除了 Changelog 中标注的不兼容版本,通常可以直接替换二进制文件进行更新。 对于 Docker 用户,只需删除旧的容器并拉取新的 Docker 镜像,然后运行它即可。 -::: - -## How to allow guest to upload files { lang="en" } -## 如何允许访客上传文件 { lang="zh-CN" } +## 如何允许访客上传文件 -:::en -Add a [meta](../guide/advanced/meta.md) record, and open `write` field. -::: -:::zh-CN 添加[元信息](../guide/advanced/meta.md),并启用 `写入`。 -::: -## How to remove "Powered by OpenList" at the bottom? { lang="en" } +## 如何去掉底部的"由 OpenList 驱动"? -## 如何去掉底部的"由 OpenList 驱动"? { lang="zh-CN" } - -:::en -According to our open source license: -Permissions of this strongest copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. **Copyright and license notices must be preserved.** Contributors provide an express grant of patent rights. When a modified version is used to provide a service over a network, the complete source code of the modified version must be made available. -::: -:::zh-CN 根据我们的开源许可: 此最强copyleft许可的权限以在同一许可下提供许可作品和修改的完整源代码为条件,其中包括使用许可作品的较大作品。**版权和许可声明必须保留** 贡献者明确授予专利权。当使用修改后的版本通过网络提供服务时,必须提供修改后版本的完整源代码。 -::: -## When adding a 189 Cloud storage: the device ID does not exist, and a secondary device verification is required { lang="en" } +## 添加 天翼云盘 云存储时:设备 ID 不存在,需要二次设备验证 -## 添加 天翼云盘 云存储时:设备 ID 不存在,需要二次设备验证 { lang="zh-CN" } - -:::en -Open the Tianyi Account website at , log in, and then disable the Device Lock.. -::: -:::zh-CN 打开天翼账号网站 ,登陆后关掉设备锁即可。 -::: -## When adding 189 Cloud PC storage: prompt need img validate code: verification code { lang="en" } - -## 添加 天翼云盘客户端 存储时:提示 need img validate code: 验证码 { lang="zh-CN" } - -:::en - -- Click Edit, write the verification code you just saw into the configuration and click Save -- Click Edit and turn on the Do not use OCR button -- Or build it yourself [**Ocr interface**](../configuration/global.md#ocr-api) -- **189 Cloud** Driver has been replaced with sliding verification code because web login has been replaced.**No longer supports OCR and manual input**, if the verification code needs to be used, please use the addition of `Cookie to log in` or use the `189 Cloud PC` Driver. Note: The 189 Cloud Driver is different from the 189 Cloud PC Driver. - -::: -:::zh-CN +## 添加 天翼云盘客户端 存储时:提示 need img validate code: 验证码 - 点击编辑,将刚才看到的验证码输入配置中,并点击保存。 - 点击编辑并开启“不使用 OCR”按钮。 - 或者自行搭建 [**OCR 接口**](../configuration/global.md#ocr-接口)。 - **天翼云盘** 驱动已被滑动验证码取代,因为网页登录方式已更改。**不再支持 OCR 和手动输入**,如果需要使用验证码,请使用 `Cookie 登录` 或者使用 `天翼云盘客户端` 驱动。注意:天翼云盘 驱动与 天翼云盘客户端 驱动不同。 -::: - -## TLS handshake timeout? / read: connection reset by peer? / dns lookup failed? / connect: connection refused / Client.Timeout exceeded while awaiting headers? / no such host? { lang="en" } - -## TLS handshake timeout? / read: connection reset by peer? / dns lookup failed? / connect: connection refused / Client.Timeout exceeded while awaiting headers? / no such host? { lang="zh-CN" } +## TLS handshake timeout? / read: connection reset by peer? / dns lookup failed? / connect: connection refused / Client.Timeout exceeded while awaiting headers? / no such host? -:::en -For network problems such as these, please troubleshoot and solve them yourself.Don't create any issues for this. -::: -:::zh-CN 诸如此类的网络问题,请自行排查解决。不要为此提出任何的issue -::: -## How to add epub reading { lang="en" } - -## 怎么添加epub阅读器 { lang="zh-CN" } - -:::en -Background --> Settings --> Preview --> Iframe preview, written behind the PDF - -```html{2-5} - /*The comma below is also oh, don’t copy this comment, start copying from the second line*/ -, - "epub": { - "EPUB.js":"/static/epub.js/viewer.html?url=$e_url" - } -``` +## 怎么添加epub阅读器 -Version 3.7.x and higher already support ".epub" reading -But you need to add it manually (because the database has already been created, it is not good to overwrite it for you, and you will make mistakes) -If it is the first installation and startup (version 3.7.x and higher), no need to add it manually -If the secondary directory reverse generation is set, please add the corresponding prefix in [site_url](../configuration/configuration.md#site-url), and then restart OpenList to take effect -::: -:::zh-CN 后台 ——>设置——>预览——>Iframe 预览,写在PDF后面 ```html{2-5} @@ -230,49 +94,20 @@ If the secondary directory reverse generation is set, please add the correspondi 但是需要自己手动添加(因为已经创建过数据库了 不好给你覆盖会出错) 如果是第一次安装启动(3.7.x版本及更高的版本)不用手动添加 如果设置了二级目录反代,请在[site_url](../configuration/configuration.md#site-url)中自行添加相应前缀,然后重启OpenList才会生效 -::: -### How to quickly locate bugs { lang="en" } - -### 如何快速定位Bug { lang="zh-CN" } - -:::en -If you find a bug, but the `log.log` log is not detailed, you can try to add the `--debug` parameter to start - -It is recommended to clear the log file `/log/log.log` in the **OpenList directory before starting with the `--debug` parameter**, so that developers can quickly locate problems later - -::: -:::zh-CN +### 如何快速定位Bug 如果发现Bug,但是`log.log`的日志不详细,可以尝试在 启动时候添加 `--debug` 参数启动 建议在使用`--debug` 参数启动之前将 **OpenList目录下的日志文件`/log/log.log` 清空**,这样方便开发者们后续快速定位问题 -::: -:::en -::: danger -When using the `--debug` parameter to start, there will be some sensitive data such as **`account password, refresh token`**, etc., so if you need to desensitize before sending it to others -::: -:::zh-CN ::: danger 使用`--debug`参数启动时,会有一些敏感数据 例如 **`账号密码,刷新令牌`** 等,所以如果在你发给别人之前需要先处理一下脱敏 ::: -:::en - -- **Windows**:`openlist.exe server --debug` -- **Linux**:`./openlist server --debug` -- **Mac**: `./openlist server --debug` -- **Docker**:`docker exec -it openlist ./openlist server --debug` (to be modified, because this command runs OpenList in an already started container, so it will not start a second OpenList) - -How to stop the relevant logs after startup? `Ctrl+C` can stop the program (or simply close the program directly) -::: -:::zh-CN - - **Windows**:`openlist.exe server --debug` - **Linux**:`./openlist server --debug` - **Mac**:`./openlist server --debug` - **Docker**:`docker exec -it openlist ./openlist server --debug` (待修改,因为该命令是在已经启动的容器内运行OpenList,所以不会启动第二个OpenList) 启动后拿到相关日志,如何停止? `Ctrl+C` 可以使程序停止运行(或者简单粗暴直接关闭程序) -::: diff --git a/pages/faq/index.md b/pages/faq/index.md index 087b8bac8..2e7933f58 100644 --- a/pages/faq/index.md +++ b/pages/faq/index.md @@ -1,5 +1,3 @@ ---- -title: FAQ ---- +# FAQ diff --git a/pages/faq/seealso.md b/pages/faq/seealso.md index a12396e90..9dbf994e5 100644 --- a/pages/faq/seealso.md +++ b/pages/faq/seealso.md @@ -1,37 +1,12 @@ --- -# This is the title of the article -title: See also -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 1 -# A page can have multiple categories categories: - faq -# A page can have multiple tags --- -### **The following resources may help you** { lang="en" } +# See also -### **以下资源可能会对您有所帮助** { lang="zh-CN" } - -:::en - -> Third party tutorials and information may be outdated and incorrect, and no warranty of availability is made here. - -[https://space.bilibili.com/36411485/channel/collectiondetail?sid=686340](https://space.bilibili.com/36411485/channel/collectiondetail?sid=686340) - -[https://anwen-anyi.github.io/](https://anwen-anyi.github.io/index/) - -[Google](https://www.google.com/search?q=openlist) - -[Bing](https://www.bing.com/search?q=openlist) - -[Baidu](https://www.baidu.com/baidu?wd=openlist) - -::: - -:::zh-CN +### **以下资源可能会对您有所帮助** > 第三方教程和信息可能已过时且不正确,此处不保证可用性。 @@ -44,5 +19,3 @@ categories: [Bing](https://www.bing.com/search?q=openlist) [Baidu](https://www.baidu.com/baidu?wd=openlist) - -::: diff --git a/pages/faq/why.md b/pages/faq/why.md index c46990f41..fde551f26 100644 --- a/pages/faq/why.md +++ b/pages/faq/why.md @@ -1,82 +1,30 @@ --- -# This is the title of the article -title: - en: Why - zh-CN: 为什么 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 1 -# A page can have multiple categories categories: - faq -# A page can have multiple tags --- -## Why I got the error `failed get storage: can't find storage with rawPath: /` when I open web pages after install/upgrade? { lang="en" } +# 为什么 -## 为什么我在安装/升级后打开网页时出现错误 `failed get storage: can't find storage with rawPath: /`? { lang="zh-CN" } +## 为什么我在安装/升级后打开网页时出现错误 `failed get storage: can't find storage with rawPath: /`? -::: en -Because you haven't added storage. Click the `Login` button at the bottom to log in and then click `Manage` button to enter the background and add storage on the storage page. -::: -::: zh-CN 因为你还没有添加任何存储,点击底部登录后再次点击管理进入后台在存储页面添加存储。 -::: -## why i lost all my storage in v3 { lang="en" } +## 为什么我在 v3 中丢失了所有存储空间 -## 为什么我在 v3 中丢失了所有存储空间 { lang="zh-CN" } - -::: en -New versions (V3 and later) are not compatible with V2, so you need to re-add your storage. -::: -::: zh-CN 新版本(V3 及更高版本)与 V2 不兼容,因此您需要重新添加存储。 -::: - -## Why I see `failed get aria2 version` error in log? { lang="en" } -## 为什么我在日志中看到 `failed get aria2 version` 错误? { lang="zh-CN" } +## 为什么我在日志中看到 `failed get aria2 version` 错误? -::: en -Because you haven't installed aria2 or the settings of aria2 are wrong. Don't worry, it won't affect the use of OpenList, you can ignore it. -::: -::: zh-CN 因为你没有安装 aria2 或者 aria2 的设置不对。不用担心,它不会影响 OpenList 的使用,你可以忽略它。 -::: -## why i got the error `Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path` when I add new storage? { lang="en" } +## 为什么我在添加新存储时收到错误 “Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path”? -## 为什么我在添加新存储时收到错误 “Failed create storage in database: UNIQUE constraint failed: x_storages.mount_path”?{ lang="zh-CN" } - -::: en -I guess the error message is pretty clear. Because [mount_path](../guide/drivers/common.md#mount-path) is **UNIQUE**. -If you see this error when adding storage, but you don't see the corresponding duplicate entry on the web page, use `Ctrl` + `F5` to force refresh page. -::: -::: zh-CN 想必错误信息表达的很清楚了。因为 [挂载路径](../guide/drivers/common.md#挂载路径) 是**独一无二**的。 如果你在添加存储时看到了这个错误,但是你并没有在管理页面看到对应的重复条目,请使用 `Ctrl` +`F5` 强制刷新页面 -::: - -## Why can't I preview the video or audio file? { lang="en" } - -## 为什么我不能预览视频或音频文件? { lang="zh-CN" } - -::: en - -- Requires a browser-supported encoding -- General browsers do not support h265 encoded video -- General browsers do not support ac3 encoded audio - -In particular, for Safari: -- For cross-origin media, content-type must be the media type, not the generic application/octet-stream. So if the content-type returned by the direct link is application/octet-stream, then Safari will not be able to play.(PS: Why Can AliyunDrive play in Safari? Because the official website of the video playback is not the use of download links, but after the transcoding of AliyunDrive) -- Aliyun disk development platform driver supports online video playback (also limited to Aliyun disk open platform driver and **Aliyun Video Previewer** option player) - -::: -::: zh-CN +## 为什么我不能预览视频或音频文件? - 需要浏览器支持的编码才可以正常预览 - 一般浏览器不支持 H.265 编码视频 @@ -87,25 +35,8 @@ In particular, for Safari: - 对于跨域媒体,content-type 必须是媒体类型,而不是通用的 application/octet-stream。所以如果直链返回的 content-type 是 application/octet-stream,那么 Safari 就无法播放了。(PS:为什么 AliyunDrive 可以在 Safari 中播放?因为官网的视频播放不是使用的下载链接,是经过 AliyunDrive 转码后的) - 阿里云盘开发平台驱动支持在线播放视频(也仅限于阿里云盘开放平台驱动并且是 **Aliyun Video Previewer** 选项播放器) -::: - -## Why can't I preview the office file? { lang="en" } +## 为什么我不能预览 Office 文件? -## 为什么我不能预览 Office 文件? { lang="zh-CN" } - -::: en -For microsoft office online viewers, you need: - -- External network access -- The document access address cannot use ip directly, it needs to be accessed through a domain name, and the port must be port 80 / 443 -- The format of the document (must be one of the following): -- Word: docx, docm, dotm, dotx -- Excel: xlsx, xlsb, xls, xlsm -- PowerPoint: pptx, ppsx, ppt, pps, pptm, potm, ppam, potx, ppsm -- Document size: Word and PowerPoint documents must be less than 10 megabytes; Excel must be less than five megabytes - -::: -::: zh-CN 对于 Microsoft Office 在线查看器,您需要: - 外部网络访问 @@ -116,129 +47,42 @@ For microsoft office online viewers, you need: - PowerPoint:pptx、ppsx、ppt、pps、pptm、potm、ppam、potx、ppsm - 文档大小:Word 和 PowerPoint 文档必须小于 10 MB; Excel 必须小于 5 MB -::: - -## Why can't I preview the PDF file? { lang="en" } - -## 为什么我不能预览 PDF 文件? { lang="zh-CN" } - -::: en - -- We use `pdf.js` as the default PDF previewer. So it requires https+cors. -- https is opened for yourself, reverse proxy or provide a certificate in the configuration file -- cors provides support for related cloud disks and cannot be modified by yourself unless the agent is turned on to use the program to transfer - -::: -::: zh-CN +## 为什么我不能预览 PDF 文件? - 我们使用 `pdf.js` 作为默认的 PDF 预览器。所以它需要 HTTPS + CORS。 - HTTPS 需要自行打开,可以使用反向代理或在配置文件中提供证书 - CORS 由相关云盘提供支持,不能自行修改,除非开启代理功能,使用程序传输 -::: - -## Why can't package download? { lang="en" } - -## 为什么无法打包下载?{ lang="zh-CN" } - -::: en -Same as pdf preview, requires https+cors support,and: +## 为什么无法打包下载? -- OpenList use stream API of browser to support package download, so it requires browser support. Usually, it is supported by new browsers, such as Chrome, Firefox, Edge, etc. - -::: -::: zh-CN 与 PDF 预览相同,需要 HTTPS 和 CORS 支持,并且: - OpenList 使用浏览器的 stream API 来支持打包下载,所以需要浏览器支持。通常这个 API 会被主流浏览器支持,例如 Chrome、Firefox、Edge 等。 -::: - -## Why does it shows `Uploading in the backend` when I upload a file? { lang="en" } - -## 为什么我上传文件时显示 `Uploading in backend`? { lang="zh-CN" } - -::: en - -- All uploads of this program use server transfer, which will consume server traffic to communicate with the corresponding storage API - -::: -::: zh-CN +## 为什么我上传文件时显示 `Uploading in backend`? - 本程序所有上传均使用服务器传输,会消耗服务器流量与对应存储的API通信 -::: - -## Why do i get `413` http code when i upload a file? { lang="en" } - -## 为什么我在上传文件时得到 HTTP 413 错误? { lang="zh-CN" } - -::: en - -- If you use a reverse proxy, you may need to specify the maximum upload file size(`client_max_body_size`) and timeout in the web server configuration -- Otherwise, it may happen that the upload is successful but the front end times out and there is no response - -::: - -::: zh-CN +## 为什么我在上传文件时得到 HTTP 413 错误? - 如果使用反向代理,可能需要在配置中指定最大上传文件大小(`client_max_body_size`)和超时时间 - 否则可能会出现上传成功但回应被web服务器阻止,导致前端超时无响应的情况 -::: - -## Why can't I download files after add `AliyunDirve` storage and get `InvalidArgument` error? { lang="en" } +## 为什么添加了 `AliyunDrive` 存储后下载不了文件,出现 `InvalidArgument` 错误? -## 为什么添加了 `AliyunDrive` 存储后下载不了文件,出现 `InvalidArgument` 错误? { lang="zh-CN" } - -::: en -::: warning -This is an outdated driver that has been abandoned, is no longer maintained, and will be removed in future versions. We recommend using the officially supported Aliyundrive Open. -[**Click to view this guide**](../guide/drivers/aliyundrive_open.md) -::: - -::: zh-CN ::: warning 这是一个过时的驱动,已废弃,不再维护,并将在未来的版本中删除。我们建议使用官方支持的阿里云盘Open驱动。 [**点击查看这篇说明**](../guide/drivers/aliyundrive_open.md) ::: -::: en -Due to referrer restrictions, mobile tokens must be used if you don't open any proxy. -::: -::: zh-CN 由于 referer 限制,如果您不打开程序的任何代理中转功能,则必须使用移动端的 `refresh token` -::: - -## Why is the uploaded file not displayed/deleted file still/modified in the root directory does not take effect? { lang="en" } -## 为什么上传的文件不显示/删除的文件还在/修改根目录不生效? { lang="zh-CN" } +## 为什么上传的文件不显示/删除的文件还在/修改根目录不生效? -::: en -There is a half-hour cache by default, which can be modified on the Add Storage page. If you need to refresh immediately, click the refresh button in the lower right corner of the directory that needs to be refreshed. -::: -::: zh-CN 默认有半个小时的缓存,可以在添加存储页面修改。如果需要立即刷新,在需要刷新的目录点击右下角的刷新按钮即可。 -::: - -## Why is it still incorrect although I input the password that get by `./openlist admin` in terminal? { lang="en" } - -## 为什么我通过在终端输入`./openlist admin`获取的密码还是不正确? { lang="zh-CN" } -::: en -Check whether the directory you run `./openlist admin` and start OpenList is the same. OpenList reads the configuration in the current directory where you run the program by default, so running in different directorys will result in different data being read. - ---- - -- **Here take Windows as an example**: For example, your OpenList file is in the **`D:\Test\OpenList\`** folder, and then you directly start the CMD command running window on the desktop, and then put the D drive Drag the OpenList inside to the command running window to start, and then the configuration file it generates is actually in **`C:\Windows\System32`** here (because CMD is here by default), and then you Obtaining the password is also obtaining the password in this folder. At the same time, because OpenList reads the password in the configuration file in the same directory as OpenList, it is naturally wrong for you to log in with the password. - - ![faq](/img/faq/alist-admin.png) -- Solution: You need to go to the folder where OpenList is located to start or check the password, for example, the above is in **`D:\Test\OpenList\`**, you have to manually go to this folder and then enter Enter the startup command or the command to view the password to obtain the correct password - - **Windows**: After going to the directory where OpenList is located, enter CMD in the top address bar and press Enter, and then use the command to obtain the password - - ![faq](/img/faq/alist-admin.gif) - - **Linux**: Use the CD command to go to the directory where OpenList is located to get the correct password +## 为什么我通过在终端输入`./openlist admin`获取的密码还是不正确? -::: -::: zh-CN 检查你运行`./openlist admin`和启动OpenList的目录是否相同。 OpenList默认会读取你运行程序的当前目录下的配置,所以在不同的目录下运行会导致读取的数据不同。 --- @@ -250,66 +94,18 @@ Check whether the directory you run `./openlist admin` and start OpenList is the - ![faq](/img/faq/alist-admin.gif) - **Linux**:使用CD命令 到OpenList所在的目录即可获取正确密码 -::: - -## Why does it prompt System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat { lang="en" } +## 为什么提示 System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat -## 为什么提示 System error: SyntaxError: Invalid regular expression: /?/: Nothing to repeat { lang="zh-CN" } - -::: en -This is caused by the conflict between some scripts in the Tampermonkey extension and the webpage code, such as some automatic answering scripts. -Please check and disable the relevant scripts on your own. -::: -::: zh-CN 由于油猴插件中的某些脚本与网页代码冲突导致的,例如某些自动答题脚本。 请自行检查并禁用相关脚本。 -::: - -## What is the difference between the two uploads? { lang="en" } - -## 两个上传有什么区别? { lang="zh-CN" } - -::: en - -1. **stream** directly puts the binary content of the file into the body, so that the backend can use very little memory, it seem to have no downsides. - -2. **form** is to wrap the file into formdata and upload it. The memory used by the backend will be more, but there is no limit on the size. - -::: -::: zh-CN +## 两个上传有什么区别? 1. **stream** 直接把文件二进制内容放进body中,这样后端可以使用很少的内存,似乎没有什么其他的缺点。 2. **form** 是把文件包裹成formdata上传,后端使用的内存会多一些,但是不限制大小。 -::: - -## What is the difference between the two Aria2? { lang="en" } - -## 两个Aria2有什么不同? { lang="zh-CN" } - -::: en - -1. Background management --> Settings --> Others: **Aria2 here is used to download resources offline to the network disk** - -2. Front-end interface --> Local settings (the gear in the lower right corner) --> **Aria2 here is used to download the resources in the network disk to the local, which is equivalent to a download method, everyone can use it without worrying about problems** - -- As for how to use the first offline download: - - For example, if you want to download some resources offline in the **`/OneDrive/TV/Test`** folder, you must first fill in the **address and secret key information** in the background and then manually enter the front end In this folder of the interface, you can see the **offline download** icon (a magnet style) in the lower right corner after entering it, click and enter the link you want to download offline - - `Description`: **Offline download, copy and upload** are all in the same way. They are first downloaded from the resource **to the temporary folder of the server** and then uploaded from the **server to the corresponding network disk** - - "Copy" can be directly transferred by the server without temporary storage for uploads that **partially do not require** to calculate the Hash value. - -```mermaid -flowchart LR - A([ready]) --> B[Download to a temporary folder] - B --> C[The server uploads] - C --> D[Upload to the network disk] - D --> E([end]) -``` - -::: -::: zh-CN +## 两个Aria2有什么不同? 1. 后台管理 --> 设置 --> 其他:**这里的Aria2是用于离线下载资源到网盘内** @@ -328,58 +124,20 @@ flowchart LR D --> E([完毕]) ``` -::: - -## RaiDrive mounts Baidu network disk, you can download files, but you cannot upload files. Throwing files to the mount directory prompts "Prompt "You need permission to perform this operation". How to solve this? { lang="en" } +## RaiDrive挂载百度网盘,可以下载文件,不能上传文件。往挂载目录丢文件提示“提示“你需要权限来执行此操作”。这个怎么解决? -## RaiDrive挂载百度网盘,可以下载文件,不能上传文件。往挂载目录丢文件提示“提示“你需要权限来执行此操作”。这个怎么解决? { lang="zh-CN" } - -::: en -Raidrive is because uploading a file will create an empty file in advance, and Baidu Netdisk does not allow the creation of an empty file. -::: -::: zh-CN Raidrive 是因为上传文件会预先创建一个空文件 而百度网盘不允许创建空文件。 -::: - -## Why is the Terabox mounted successfully but the content is not displayed? { lang="en" } - -## 为什么 Terabox 挂载成功了 但是不显示内容? { lang="zh-CN" } - -::: en -Because **`Terabox`** restricts IP, if you access it from a non-overseas IP, you cannot access it, and naturally the content will not be displayed. -(Overseas machines can be used. If you have to use local machines (or domestic machines) to build, you can find a way to let OpenList eat **proxy**) +## 为什么 Terabox 挂载成功了 但是不显示内容? -**Note: Check the relevant settings to ensure that traffic related to '\*.terabox.com' goes through the **proxy\***\* -::: -::: zh-CN 因为 **`Terabox`\*\* 限制IP,如果你是本机非海外IP访问,是无法访问的,自然不会显示内容。 (可以用海外的机器,如果非得用本机(或者国内的机器)搭建,那你自己想办法让OpenList吃到**proxy**就可以) **注意:检查相关设置以确保'\*.terabox.com'相关流量经过**proxy\*\*\*\* -::: - -## Open OpenList prompt System error: TypeError: n.replaceAll is not a function? { lang="en" } -## 打开 OpenList 提示 System error: TypeError:n.replaceAll is not a function? { lang="zh-CN" } +## 打开 OpenList 提示 System error: TypeError:n.replaceAll is not a function? -::: en -Your browser kernel version is too low, just add the following to the custom head. - -```html - - -``` - -If you think it is too slow, you can change to Ali Cloud - -```html - -``` - -::: -::: zh-CN 你的浏览器内核版本太低,将如下添加到自定义头部即可. ```html @@ -393,83 +151,31 @@ If you think it is too slow, you can change to Ali Cloud ``` -::: +## Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. -## Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. { lang="en" } - -## Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later. { lang="zh-CN" } - -::: en -![faq](/img/faq/error.png) - -If you enter the wrong password for 6 consecutive logins, it will be locked, and you can reset it by restarting OpenList. -::: -::: zh-CN ![faq](/img/faq/error.png) 连续登录输入6次密码错误就会锁定,重启OpenList即可重置。 -::: - -## When adding a file to upload, there is an option of "add as task", what does it do? { lang="en" } -## 添加文件上传时,有一个“添加为任务“的选项,是做什么的? { lang="zh-CN" } - -::: en - -- After checking, upload to the server (the machine where OpenList is built), and then when uploaded by the server to the network disk, it seems to be visible in the background (it seems to be like this) -- You will not see it if it is not checked. For details, you can see the problem in the picture below. - -![faq](/img/faq/add.jpeg) -::: -::: zh-CN +## 添加文件上传时,有一个“添加为任务“的选项,是做什么的? - 勾选后上传到服务器(搭建OpenList的机器),再由服务器上传时到网盘时后台似乎能看到(好像是这样的) - 未勾选不会看到,具体的大家可以看下图应该能看出什么问题. ![faq](/img/faq/add.jpeg) -::: - -## I am asked to enter the OTP code when I log in. What is the reason? { lang="en" } - -## 登陆时要求输入OTP代码,请问是什么原因? { lang="zh-CN" } -::: en -Are you logging into someone else's account? If it is your own, please enter the 2FA (two-step verification) verification code +## 登陆时要求输入OTP代码,请问是什么原因? -What? Don't know the verification code? [**Click to view this guide**](../guide/advanced/2fa.md) -::: -::: zh-CN 你是不是在登录别人的账号?如果是自己的请输入2FA(两步验证)验证码 什么?不知道验证码?[**点击查看这篇说明**](../guide/advanced/2fa.md) -::: -## Use **`sqlite3`** to find that there are more than two files in the data folder, data.db-shm and data.db-wal { lang="en" } +## 使用 **`sqlite3`** 发现 data 文件夹里面出现 data.db-shm、data.db-wal 两个多的文件 -## 使用 **`sqlite3`** 发现 data 文件夹里面出现 data.db-shm、data.db-wal 两个多的文件 { lang="zh-CN" } - -::: en -These are temporary files created by SQLite in WAL mode to support database transaction processing and concurrent access. The .db-shm file is a shared memory file used to store temporary data, while the .db-wal file is a write-ahead log file that records transaction changes for rollback in case of transaction failure. These two files are associated with the .db file, and it is not recommended to delete them arbitrarily. -::: -::: zh-CN 这是SQLite在WAL模式下创建的临时文件,用于支持数据库的事务处理和并发访问。.db-shm是一个共享内存文件,用于存储临时数据,而.db-wal是一个写前日志文件,用于记录事务的变更,以便在事务失败时进行回滚。这两个文件和.db文件相关联,不建议随意删除。 -::: - -## **How do I set it so that visitors can only see the content after logging in?** { lang="en" } - -## **想让游客登录后才能看到内容怎么设置?** { lang="zh-CN" } - -::: en -There is always one of the following three versions that suits you. Eat it quickly! ! -1. Point the directory seen by **`guest`** users to an empty folder, and then write a readme description, don’t ask how to do it yourself[Click to view](#how-to-create-a-completely-blank-folder) +## **想让游客登录后才能看到内容怎么设置?** -2. Directly encrypt the root directory in the meta information - -3. Directly deactivate the **`guest`** user, which will force a jump to the login page (version 3.10.1 and above) - Find a way by yourself Welcome to provide new ways~~ - -::: -::: zh-CN 以下三种版本总有一款适合你 快速速食用吧!! 4. 将 **`guest`** 用户看到的目录 指向一个空文件夹,然后写一个readme说明,别问怎么弄自行操作[点击查看](#如何新建一个完全空白的文件夹) @@ -477,25 +183,8 @@ There is always one of the following three versions that suits you. Eat it quick 6. 直接将 **`guest`** 用户停用,这样会强制跳转到登录页面(3.10.1及以上版本) - 自行寻找办法 欢迎提供新的办法~~ -::: +## 百度云盘非官方下载报错出现"hit black userlist , hit illeage dlna" -## Baidu cloud disk unofficial download error appears "hit black userlist, hit illegal dlna" { lang="en" } - -## 百度云盘非官方下载报错出现"hit black userlist , hit illeage dlna" { lang="zh-CN" } - -::: en -It should be repaired harmoniously, but the interface discoverer said to wait for 24 hours to try, if it still doesn't work, it is really repaired harmoniously. - -```json -{ - "error_code": 31329, - "error_msg": "hit black userlist , hit illegal dlna", - "request_id": 8942439159334999112 -} -``` - -::: -::: zh-CN 应该是和谐修复了,但是接口发现者说等待24小时试试看,如果还是不行就是真的和谐修复了。 ```json @@ -506,27 +195,8 @@ It should be repaired harmoniously, but the interface discoverer said to wait fo } ``` -::: - -## How to create a completely blank folder { lang="en" } - -## 如何新建一个完全空白的文件夹 { lang="zh-CN" } - -::: en -two ways +## 如何新建一个完全空白的文件夹 -1. Mount a local storage, select the folder is empty -2. Select virtual storage and directly change **"number of files" "number of folders"** to 0, save and then ignore the pop-up interface - - What does **Virtual Storage** do? It is generally used during testing. Don’t use it indiscriminately if you don’t understand the normal production environment.... - -How to add **`Readme`** instructions to an empty folder - -1. If you choose local storage, you can consider throwing a Readme.md file into it. If there is an update but the refresh is slow, use the second method if it is too slow -2. Meta information Select this folder, there are instructions below to write it yourself - -What is the use of creating this blank folder? It can be used in conjunction with [**How to set up visitors to access the content after logging in, use the first method**](#how-do-i-set-it-so-that-visitors-can-only-see-the-content-after-logging-in) -::: -::: zh-CN 两种方法 1. 挂载一个本地存储,选择文件夹里面是空的就可以 @@ -539,50 +209,17 @@ What is the use of creating this blank folder? It can be used in conjunction wit 2. 元信息 选择这个文件夹,下面有说明自己写就行 建这个空白文件夹有啥用?可以配合[**想让游客登录后才能看到内容怎么设置的第一条办法使用**](#想让游客登录后才能看到内容怎么设置)使用 -::: - -## lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./openlist) or accept: function not implemented { lang="en" } - -## lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./openlist)或者 accept: function not implemented { lang="zh-CN" } -::: en -Consider using the **musl** version as an alternative due to the version of the **glibc** on your platform is too low,or use docker version. +## lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./openlist)或者 accept: function not implemented -::: -::: zh-CN 考虑使用 **musl** 版本作为替代,因为在您的平台上的 **glibc** 版本太低,或者使用docker版本。 -::: - -## What is the extra sign parameter after the file now? { lang="en" } - -## 现在文件后面多了个 sign 的参数是干嘛的? { lang="zh-CN" } +## 现在文件后面多了个 sign 的参数是干嘛的? -::: en -Encrypted for account security, you can view the details by [click to view](../configuration/global.md#sign-all) -::: -::: zh-CN 加密了为了账号安全一些,查看详情可以[点击查看](../configuration/global.md#签名所有) -::: - -## Prompt when adding Aliyun drive (shared) Prompt Post "https://auth.aliyundrive.com/v2/account/token": net/http: TLS handshake timeout { lang="en" } - -## 添加阿里云盘(分享)时提示 提示Post "https://auth.aliyundrive.com/v2/account/token": net/http: TLS handshake timeout { lang="zh-CN" } -::: en -Reason: The VPS is located outside mainland China, and IPv6 is enabled -Workaround: -**Method 1**: Add the following three items in the vps hosts and restart the network or restart the vps +## 添加阿里云盘(分享)时提示 提示Post "https://auth.aliyundrive.com/v2/account/token": net/http: TLS handshake timeout -- 47.94.245.197 api.aliyundrive.com - -- 47.94.245.197 auth.aliyundrive.com - -- 121.89.3.30 cn-beijing-data.aliyundrive.net - -**Method 2**: Turn off and disable IPv6 of vps -::: -::: zh-CN 原因:VPS在中国大陆境外,且启用了IPv6 解决方法: 方法一:在vps的hosts中添加以下三条并重启网络或者重启vps @@ -594,190 +231,75 @@ Workaround: - 121.89.3.30 cn-beijing-data.aliyundrive.net 方法二:关闭并且禁用vps的IPv6 -::: - -## 123 When the cloud disk is mounted, json: cannot unmarshal string into Go struct field TokenResp.data of type struct { Token string "json:\"token\"" } { lang="en" } - -## 123云盘挂载时出现 json: cannot unmarshal string into Go struct field TokenResp.data of type struct { Token string "json:\"token\"" } { lang="zh-CN" } - -::: en -![faq](/img/faq/123error.png) -There is a high probability that your device that builds OpenList is temporarily blocked, frozen, blocked, or restricted. Don't mount it yet, wait a few hours and try again or wait a day +## 123云盘挂载时出现 json: cannot unmarshal string into Go struct field TokenResp.data of type struct { Token string "json:\"token\"" } -(Conclusions from the above questions have been tested) -::: -::: zh-CN ![faq](/img/faq/123error.png) 大概率是你的这台搭建OpenList的设备 暂时被 拉黑,冻结,封禁,限制了。先不要挂载了,等几个小时再试试看或者等一天 (以上问题已经经过测试得出的结论) -::: - -## Why doesn't PikPak have a name { lang="en" } -## 为何 PikPak 下载文件不显示文件名? { lang="zh-CN" } +## 为何 PikPak 下载文件不显示文件名? -::: en -Because PikPak's server does not set the filename in the `content-disposition` header, this is a PikPak problem and not an OpenList, so it cannot be fixed -::: -::: zh-CN 因为 PikPak 的服务器在 content-disposition 头中没有设置文件名,所以这是 PikPak 的问题 而不是 OpenList,故无法解决 -::: - -## How to query your own OpenList version number { lang="en" } - -## 如何查询自己的OpenList版本号 { lang="zh-CN" } -::: en - -1. Use the command `openlist version`, the console will output -2. OpenList Manage --> Settings --> Site --> `Version` option - -Why is the old version of OpenList still after the update? Generally, docker encounters this problem. You can [**refer to this**](../guide/installation/docker.md#update) -::: -::: zh-CN +## 如何查询自己的OpenList版本号 1. 使用命令 `openlist version`,控制台会输出 2. OpenList后台 --> 设置 --> 站点 --> `版本`选项 为什么更新后还是旧版的OpenList,一般遇到这个问题的都是docker,可以[**参考这个**](../guide/installation/docker.md#更新) -::: - -## OpenList's download "Download" option suddenly blank, no content { lang="en" } - -## OpenList的下载"Download"选项突然空白,没有内容 { lang="zh-CN" } - -::: en -Check your plug-ins, whether there are ad blocking plug-ins, for example: **AdGuard** and others -Through the console, it is found that "**https://g.alicdn.com/IMM/office-js/1.1.5/aliyun-web-office-sdk.min.js**" has been blocked, +## OpenList的下载"Download"选项突然空白,没有内容 -Maybe other links are blocked, manually add the blocked domain name to the whitelist or close the plug-in, or it may be that the plug-in is convulsed - -![faq](/img/faq/download.png) -::: -::: zh-CN 检查你的插件,是否有广告拦截插件,例如:**AdGuard**等其他的 通过控制台发现"**https://g.alicdn.com/IMM/office-js/1.1.5/aliyun-web-office-sdk.min.js**" 被拦截了, 也许拦截了其它链接,手动将被拦截域名添加到白名单或者关闭插件,也可能是插件抽风了 ![faq](/img/faq/download.png) -::: -## Why do prompts appear after clicking "Login success" and "Guest is disabled, login please" { lang="en" } +## 为什么在点击登录后同时出现提示 "登录成功" 和 "Guest is disabled, login please" -## 为什么在点击登录后同时出现提示 "登录成功" 和 "Guest is disabled, login please" { lang="zh-CN" } - -::: en -Please check whether your site is connected to a `CDN`. If so, please remove the CDN cache. - -Connecting OpenList to a CDN is not recommended for non-professionals. [See here](https://t.me/OpenListOfficial/23). - -::: - -::: zh-CN 请检查你的站点是否接入了 `CDN`。如有,请去掉CDN缓存。 不建议非专业人士给OpenList接入CDN。详见[此处](https://t.me/OpenListOfficial/23)。 -::: - -## Why has the "Add" button on the sharing management page been disabled? { lang="en" } +## 为什么分享管理页面的添加按钮被禁用了? -## 为什么分享管理页面的添加按钮被禁用了? { lang="zh-CN" } - -::: en -Your account does not have "Sharing" permissions. - -- If you are an administrator, please go to the User Management interface to enable the required permissions for yourself. -- If you are a regular user, please contact your administrator to request access. - -::: - -::: zh-CN 你的账号没有“分享”权限。 - 如果你是管理员,需要在用户管理界面为自己开启相应权限。 - 如果你是普通用户,请联系管理员。 -::: - -## Why can't I use a WebDAV client to manage (writing operations) files in OpenList? { lang="en" } - -## 为什么我无法使用 WebDAV 客户端管理(特指写入操作) OpenList 中的文件? { lang="zh-CN" } +## 为什么我无法使用 WebDAV 客户端管理(特指写入操作) OpenList 中的文件? -::: en -The reason is the same as the previous one: you need the "WebDAV Manage" permission. -::: - -::: zh-CN 原因与上一条问题相同,你需要“WebDAV管理”权限。 -::: - -## Why is my Markdown file preview showing garbled text? { lang="en" } - -## Why aren't lists in my Markdown file visible during preview? { lang="en" } - -## 为什么预览 Markdown 文件会出现乱码? { lang="zh-CN" } - -## 为什么预览时看不到 Markdown 文件中的列表? { lang="zh-CN" } -::: en -This phenomenon is mainly observed in Markdown files that include tables or code. As a security measure to prevent the execution of potentially malicious scripts from untrusted sources, the system has XSS protection enabled by default. +## 为什么预览 Markdown 文件会出现乱码? -This mechanism can be disabled by navigating to "**Settings / Preview**" and deactivating the "**Filter scripts in README**" option. +## 为什么预览时看不到 Markdown 文件中的列表? -Please note that disabling this protective measure will make your device vulnerable to XSS (cross-site scripting) attacks. Users are fully responsible for the security of their own devices. -::: - -::: zh-CN 这一问题主要出现在 Markdown 文件中存在表格或代码的情况,为了防止用户在不知情的情况下点击来源不明的 Markdown 文件,导致其中可能存在的恶意代码被执行,系统默认开启了 XSS 保护。 你可以在“**设置 / 预览**”中关闭“**过滤 README 文件中的脚本**”选项以关闭这一机制。 **关闭这一保护机制后,你的设备可能会遭受到XSS攻击,请为自己的设备安全负责。** -::: - -## Why can't I connect to OpenList via an FTP client even after I've filled in the FTP mount configuration? { lang="en" } -## 为什么我填写了 FTP 挂载相关配置,但依然无法通过 FTP 客户端连接 OpenList? { lang="zh-CN" } - -::: en -Besides configuring the settings in the admin panel, you must also set `enable` to `true` under the `ftp` section in the configuration file (`config.json`) to activate the FTP connection feature. - -Additionally, any changes made in the admin panel require a restart of OpenList to take effect. - -If you wish to connect using the SFTP protocol, please enable it by setting `enable` to `true` in the `sftp`(rather than `ftp`) section of the configuration file. -::: +## 为什么我填写了 FTP 挂载相关配置,但依然无法通过 FTP 客户端连接 OpenList? -::: zh-CN 除了填写管理界面中的相关设置外,你还需要将配置文件(`config.json`)中`ftp`子项中的`enable`设置为`true`才能开启 FTP 连接功能。 此外,管理界面中的配置项修改完毕后,需要重新启动 OpenList 才能生效。 如果你需要使用 SFTP 协议连接 OpenList,请打开配置文件(`config.json`)中`sftp`(而不是`ftp`)子项中的`enable`。 -::: - -## Why are the filenames in my ZIP file showing up as garbled text/characters when I preview it? { lang="en" } - -## 为什么预览 ZIP 压缩文件时,文件名显示为乱码? { lang="zh-CN" } - -::: en -Older versions of the ZIP format do not mandate UTF-8 encoding for filenames, which can lead to decoding problems for non-ASCII characters. -To resolve this, you can adjust the "**Alternative Encoding for ZIP Files**" setting under "**Settings / Preview**". +## 为什么预览 ZIP 压缩文件时,文件名显示为乱码? -For Simplified Chinese users, you can fix this issue by setting this value to "**GBK**" in most cases. -::: - -::: zh-CN 较旧版本的 ZIP 文件并不强制要求文件名采用 UTF-8 编码,此时非 ASCII 字符的解码会出现问题。 你可以在“**设置 / 预览**”中调整“**ZIP 文件备选编码**”配置项的值来规定系统此时采用什么编码来解码文件名。 对于简体中文用户来说,将此项设置为“**GBK**”就可以解决大多数情况下文件名乱码的问题。 -::: diff --git a/pages/guide/advanced/2fa.md b/pages/guide/advanced/2fa.md index 9752adbc2..d366e1c56 100644 --- a/pages/guide/advanced/2fa.md +++ b/pages/guide/advanced/2fa.md @@ -1,28 +1,14 @@ --- -title: - en: 2Fa - zh-CN: 双因素身份验证 categories: - guide - advanced top: 110 --- -## Enable 2FA verification { lang="en" } +# 双因素身份验证 -## 启用 2FA 验证 { lang="zh-CN" } +## 启用 2FA 验证 -::: en -To enable 2-factor authentication, you need to install a app that support 2FA (such as [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2),[Microsoft Authenticator](https://support.microsoft.com/en-us/account-billing/download-and-install-the-microsoft-authenticator-app-351498fc-850a-45da-b7b6-27e523b8702a)), on your phone. - -Then login to OpenList manage and goto `Profile` page, click `Enable 2FA` button, scan the QR code with your 2FA app, and enter the code generated by your 2FA app. - -Finally, click `Verify` button to enable 2FA. - -- Each user can set a different "2FA" verification, how to cancel see the method below - -::: -::: zh-CN 要启用双因素身份验证,需要在手机上安装支持 TOTP 的验证器,例如 [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2),[Microsoft Authenticator](https://support.microsoft.com/zh-cn/account-billing/%E4%B8%8B%E8%BD%BD%E5%B9%B6%E5%AE%89%E8%A3%85microsoft-authenticator%E5%BA%94%E7%94%A8-351498fc-850a-45da-b7b6-27e523b8702a) 然后登录 OpenList 管理并进入“个人资料”页面,点击“启用 2FA”按钮,用你的 2FA 应用扫描二维码,输入你的 2FA 应用生成的代码。 @@ -31,48 +17,7 @@ Finally, click `Verify` button to enable 2FA. - 每个用户都可以设置不同的"2FA"验证,如何解除看下面的方法 -::: - -## Cancel 2FA verification { lang="en" } - -## 解除 2FA 验证 { lang="zh-CN" } - -::: en - -**1. Forgot 2FA for a non-Admin account:** - -If you’ve forgotten the 2FA for a non-admin account, you can contact an administrator to clear it. The steps are as follows: - -- Go to the backend → Select "Users" → Click **"Cancel Two-Factor Authentication"**. - -If the 2FA is lost for an admin account, please refer to step 2 for instructions on clearing it. - ---- - -**2. How to access the folder where OpenList is located and clear 2FA:** - -- **Windows**: Navigate to the folder where OpenList is located and enter the command: - `openlist.exe cancel2fa` - -- **Linux**: Similarly, go to the OpenList folder and enter the command: - `./openlist cancel2fa` - -- **Docker**: In a Docker environment, enter the command: - `docker exec -it openlist ./openlist cancel2fa` - -After running these commands, a restart will apply the changes. - ---- - -**3. Actions after canceling 2FA:** - -In the "Users" section of the backend, you will find the **"Cancel Two-Factor Authentication"** option. After canceling, if issues persist, try restarting OpenList. - -**Note**: If you don’t see the **"Cancel Two-Factor Authentication"** option, please check if you’re using the latest version. If not, please update your version. - -::: - -::: zh-CN +## 解除 2FA 验证 **1. 忘记非Admin账号的2FA密码:** @@ -104,5 +49,3 @@ In the "Users" section of the backend, you will find the **"Cancel Two-Factor Au 进入后台“用户”页面后,您可以看到 **“取消两步验证”** 的选项。取消后,若仍然遇到问题,可以尝试重新启动OpenList。 **注意**:如无法看到 **“取消两步验证”** 选项,请检查是否使用的是最新版本,如不是,请更新版本。 - -::: diff --git a/pages/guide/advanced/alias.md b/pages/guide/advanced/alias.md index 1bebc0339..8a1c5d968 100644 --- a/pages/guide/advanced/alias.md +++ b/pages/guide/advanced/alias.md @@ -1,11 +1,10 @@ --- -title: - en: Alias - zh-CN: 别名 categories: - guide - advanced top: 120 --- +# 别名 + diff --git a/pages/guide/advanced/backup.md b/pages/guide/advanced/backup.md index 9564dd34d..3d66ba1c9 100644 --- a/pages/guide/advanced/backup.md +++ b/pages/guide/advanced/backup.md @@ -1,29 +1,14 @@ --- -title: - en: Backup&Restore - zh-CN: 备份&恢复 categories: - guide - advanced top: 20 --- -## Method 1-Built-in { lang="en" } +# 备份&恢复 -## 方法1-内置 { lang="zh-CN" } +## 方法1-内置 -::: en -Use `OpenList` to manage `Backup/Restore` options - -1. Backup: Backup OpenList data (**does not include index**) -2. Recovery: Backup files before recovery -3. Override: Select override when recovery, and will override the user information -4. Encryption Password:Fill in the `ENCRYPTION PASSWORD` Export, the information will be encrypted when exporting.You also need to enter the `ENCRYPTION PASSWORD` first before importing. - - ![](/img/advanced/backup.png) - -::: -::: zh-CN 使用`OpenList`后台的内置 `备份/恢复`选项 1. 备份:进行 OpenList 数据进行备份(**不包含索引**) @@ -33,23 +18,8 @@ Use `OpenList` to manage `Backup/Restore` options ![](/img/advanced/backup.png) -::: - -## Method 2-Sqlite3 { lang="en" } - -## 方法2-Sqlite3 { lang="zh-CN" } - -::: en -Back up the `data/data.db` database in the `OpenList` directory, and wait for the new environment to replace it directly (**contains index data**) +## 方法2-Sqlite3 -- If there are two files, if there are `data.db-shm` and `data.db-wal`, they must be backup together. -- It is recommended to stop running OpenList and then backup, so that the two files of `data.db-shm` and`data.db-wal` will be merged with the `data.db` - - ![](/img/advanced/sqlite3.png) - -::: - -::: zh-CN 直接将`OpenList`目录下的 data/`data.db`数据库进行备份,等到了新环境直接进行替换即可(**包含索引数据**) - 现在高版本如果有`data.db-shm`和`data.db-wal`这两个文件也要一起进行备份,恢复时也是一起替换 @@ -57,15 +27,6 @@ Back up the `data/data.db` database in the `OpenList` directory, and wait for th ![](/img/advanced/sqlite3.png) -::: - -## Method 3-Other database { lang="en" } - -## 方法3-其他数据库 { lang="zh-CN" } +## 方法3-其他数据库 -::: en -Other, if you use `mysql, postgres` and other non `sqlite database`, please solve it by yourself (**include indexing data**) -::: -::: zh-CN 其他,如果你使用了 `MySQL,Postgres` 等非`sqlite数据库`,请自行解决(**包含索引数据**) -::: diff --git a/pages/guide/advanced/balance.md b/pages/guide/advanced/balance.md index 2377b1e73..0a8872c95 100644 --- a/pages/guide/advanced/balance.md +++ b/pages/guide/advanced/balance.md @@ -1,44 +1,14 @@ --- -title: - en: Load balancing - zh-CN: 负载均衡 categories: - guide - advanced top: 90 --- -## Link load balancing { lang="en" } +# 负载均衡 -## 链路负载均衡 { lang="zh-CN" } +## 链路负载均衡 -::: en -Link load balancing distributes traffic across multiple links to alleviate bandwidth pressure on any single link. This load balancing mechanism is only applicable when traffic arrives at the same storage service via multiple distinct links. The system assumes that the drivers participating in the load balancing can always automatically maintain content consistency and will simply forward all requests in a round-robin manner: - -```mermaid -graph LR - User -->|RW file1| OpenList - OpenList -->|Write file1| Link1 - OpenList -->|Read file1| Link2 - Link1 -->|Write file1| Serv[Storage Service] - Link2 -->|Read file1| Serv[Storage Service] -``` - -To perform this load balancing mechanism, mount the first driver normally, and then add the remaining load-balancing drivers using a mount path formatted as `first storage mount path + .balance + any additional content`. - -E.g: - -- Storage 1: `test` -- Storage 2: `test.balance1` -- Storage 3: `test.balance2` -- Storage 4: `test.balance3` -- ... -- Storage n: `test.balancen` - -![](/img/advanced/balance.png) -The first is marked with a red box. It is the main mount, which is displayed on the front page. The remaining nine are the first load balancing on the first one. -::: -::: zh-CN 链路负载均衡用于在多条链路上分担流量,以减轻单条链路的带宽压力。这种负载均衡机制仅用于流量从多条不同的链路抵达同一个存储服务的情况,系统将假定参与负载均衡的驱动总能自动保持内容一致,并简单地轮询转发所有请求: ```mermaid @@ -63,43 +33,9 @@ graph LR ![](/img/advanced/balance.png) 第一个带红框标记的为主挂载,也就是在前端页面显示的,后面剩下的九个就是对第一个进行负载均衡。 -::: - -## Storage load balancing { lang="en" } -## 存储负载均衡 { lang="zh-CN" } - -::: en -Storage Load Balancing is used to distribute storage occupancy across multiple storage services, achieving the effect of abstracting multiple storage spaces into one large storage space with a capacity equal to the sum of all individual spaces (similar to RAID 0). The system will assign each uploaded file to a random storage service and forward all read and modification requests for that file to that specific service: - -```mermaid -graph LR - User -->|Requests| OpenList - OpenList -->|Write file1| Serv1[Storage Service 1] - OpenList -->|Write file2| Serv2[Storage Service 2] - OpenList -->|Read file1| Serv1 - OpenList -->|Rename file1| Serv1 - OpenList -->|Read file2| Serv2 -``` - -This type of load balancing can be implemented using the putting load balancing feature of the [Alias](/guide/drivers/alias) driver, with the following configuration: - -- **Reading conflict policy**: **Get the file corresponding to the first conflict path** (The load balancing function of the Rading conflict policy is used to implement the load balancing described in the [Multi-source reading load balancing](/guide/advanced/balance#multi-source-reading-load-balancing) section. Based on the principles of the two load balancing schemes, enabling both simultaneously does not achieve a 1+1>=2 effect). -- **Writing conflict policy**: Choose either **Allow full conflict paths** or **Write into all conflict paths** (If the **Write into the first conflict path** policy is used, folder creation operations will only be forwarded to one driver. This will cause that folder and its descendant folders to be unable to continue load balancing as they won't exist on other drivers). -- **Putting conflict policy**: Choose one of **Random load balancing**, **Weighted random load balancing based on remaining space**, or **Strict weighted random load balancing based on remaining space** according to your needs. - -The effect achieved with the above configuration: - -```mermaid -graph LR - OpenList -->|File1,File2,File3| Alias - Alias -->|File3| Driver1[Driver 1] - Alias -->|File1| Driver2[Driver 2] - Alias -->|File2| Driver3[Driver 3] -``` +## 存储负载均衡 -::: -::: zh-CN 存储负载均衡用于在多个存储服务上分担空间占用,实现多个存储空间抽象为一个容量为所有存储空间之和的大存储空间的效果(类似于 RAID 0),系统将为每个上传的文件分配一个随机的存储服务,并将对于该文件的读取和修改请求全部转发到这个存储服务上: ```mermaid @@ -128,31 +64,8 @@ graph LR Alias -->|文件2| Driver3[驱动3] ``` -::: - -#### Load balancing by file chunks { lang="en" } - -#### 按文件块负载均衡 { lang="zh-CN" } - -::: en -Sometimes, the files that need to be load-balanced across storage are relatively large, and performing load balancing on a per-file basis is not granular enough. You can use the [Chunk](/guide/drivers/chunk) driver to split files into fixed-size blocks and then apply storage load balancing to these blocks. The specific configuration is as follows: - -- **Chunk**: In the remote path, fill in the mount path of the **Alias** driver. Configure other settings as needed. -- **Alias**: Keep the configuration consistent with the description above. - -The achieved effect is: - -```mermaid -graph LR - OpenList -->|A Large File| Chunk - Chunk -->|Block1, Block2, Block3| Alias - Alias -->|Block1| Driver1[Driver 1] - Alias -->|Block3| Driver2[Driver 2] - Alias -->|Block2| Driver3[Driver 3] -``` +#### 按文件块负载均衡 -::: -::: zh-CN 有时需要被负载均衡存储的文件比较大,以文件为单位进行负载均衡颗粒度不够细,可以使用[分块](/guide/drivers/chunk)驱动将文件划分为固定大小的文件块,再对文件块进行存储负载均衡,具体配置如下: - 分块:远程路径填写**别名**驱动的挂载路径,其它配置项按需填写。 @@ -169,34 +82,8 @@ graph LR Alias -->|文件块2| Driver3[驱动3] ``` -::: +## 多源读取负载均衡 -## Multi-source reading load balancing { lang="en" } - -## 多源读取负载均衡 { lang="zh-CN" } - -::: en -Multi-source reading load balancing allows copies of a file to be distributed across several different storage services. When a user accesses the file, the system randomly selects one of the copies to return, thereby reducing the uplink bandwidth pressure on any single storage service (similar to RAID 1). The key difference between this load balancing strategy and [Link load balancing](/guide/advanced/balance#link-load-balancing) is that since the load-balanced storage services are treated as multiple, independent file systems with no automatic synchronization, OpenList will forward write operations to **all** storage services, rather than to just one of them: - -```mermaid -graph LR - User -->|RW file1,file2| OpenList - OpenList -->|Write file1| Serv1[Storage Service 1] - OpenList -->|Write file2| Serv1 - OpenList -->|Write file1| Serv2[Storage Service 2] - OpenList -->|Write file2| Serv2 - OpenList -->|Read file1| Serv1 - OpenList -->|Read file2| Serv2 -``` - -This type of load balancing can be implemented using the reading load balancing feature of the [Alias](/guide/drivers/alias) driver, with the following configuration: - -- **Reading conflict policy**: Choose either **Load balancing on a per-file basis** or **Load balancing on a per-part basis**. -- **Writing conflict policy**: Choose either **Allow full conflict paths** or **Write into all conflict paths** (If the **Write into the first conflict path** policy is used, folder creation operations will be forwarded to only one driver. This will prevent that folder and its descendant folders from continuing to participate in load balancing, as they won't exist on other drivers). -- **Putting conflict policy**: Choose either **Allow full conflict paths** or **Put into all conflict paths** (The load balancing function of the Putting conflict policy is used to implement the load balancing described in the [Storage Load Balancing](/guide/advanced/balance#storage-load-balancing) section. Based on the principles of the two load balancing schemes, enabling both simultaneously does not achieve a 1+1>=2 effect). - -::: -::: zh-CN 多源读取负载均衡可以将文件的副本分散在若干不同的存储服务上,当用户访问文件时,系统会随机选择其中一个文件返回,从而降低单个存储服务的上行带宽压力(类似于 RAID 1)。这种负载均衡策略与[链路负载均衡](/guide/advanced/balance#链路负载均衡)的最大不同之处在于,由于负载均衡存储服务被视为多个没有关联,无法自动同步的文件系统,OpenList 将会向**所有**存储服务转发写入操作,而不是向其中一个转发: ```mermaid @@ -215,5 +102,3 @@ graph LR - 读取冲突策略:**按文件负载均衡**和**按分片负载均衡**二选一。 - 写入冲突策略:**仅允许全冲突路径**和**写入所有有效路径**二选一(如果使用**写入首个有效路径**策略,创建文件夹操作将会仅被转发到一个驱动上,这将导致该文件夹及其子孙文件夹由于不存在于其它驱动上,而无法继续负载均衡)。 - 上传冲突策略:**仅允许全冲突路径**和**上传到所有有效路径**二选一(上传冲突策略的负载均衡功能用于实现[存储负载均衡](/guide/advanced/balance#存储负载均衡)章节的负载均衡功能,按照两种负载均衡方案的原理,同时开启两种负载均衡功能并不能实现1+1>=2的效果)。 - -::: diff --git a/pages/guide/advanced/ftp.md b/pages/guide/advanced/ftp.md index f50b71dea..f30fbe8a7 100644 --- a/pages/guide/advanced/ftp.md +++ b/pages/guide/advanced/ftp.md @@ -1,22 +1,12 @@ --- -title: - en: FTP / SFTP - zh-CN: FTP / SFTP categories: - guide - advanced top: 30 --- -::: en -::: tip -Any adjustments made to FTP-related configurations on the web management page must restart OpenList to take effect. - -When using FTP for downloading, only the local proxy will be used. - -::: +# FTP / SFTP -::: zh-CN ::: tip 所有在网页端管理页面对 FTP 相关配置进行的调整必须重启 OpenList 才会生效。 @@ -24,28 +14,7 @@ When using FTP for downloading, only the local proxy will be used. ::: -## FTP Configurations { lang="en" } - -## FTP 配置文件 { lang="zh-CN" } - -::: en - -| Field | Meaning | Example Value | -| --------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | -| enable | Whether to enable | `true` / `false` | -| listen | (Allowed access IP mask): port | `":5221"` (default) / `"0.0.0.0:21"` / `"127.0.0.1:2121"` | -| find_pasv_port_attempts | Maximum attempts to find a port due to port conflict in passive transfer | `50` | -| active_transfer_port_non_20 | Enable ports other than 20 for active transfer ports | `true` / `false` | -| idle_timeout | Maximum idle time (in seconds) without client requests | `900` | -| connection_timeout | Connection timeout time | `30` | -| disable_active_mode | Disable active transfer mode | `true` / `false` | -| default_transfer_binary | Default transfer in binary mode | `true` / `false` | -| enable_active_conn_ip_check | Check the IP of the client for data stream TCP connection in active transfer mode | `true` / `false` | -| enable_pasv_conn_ip_check | Check the IP of the client for data stream TCP connection in passive transfer mode | `true` / `false` | - -::: - -::: zh-CN +## FTP 配置文件 | 字段 | 含义 | 示例值 | | --------------------------- | ------------------------------------------------ | ------------------------------------------------------- | @@ -60,39 +29,12 @@ When using FTP for downloading, only the local proxy will be used. | enable_active_conn_ip_check | 主动传输模式下对数据流TCP连接的客户端进行IP检查 | `true` / `false` | | enable_pasv_conn_ip_check | 被动传输模式下对数据流TCP连接的客户端进行IP检查 | `true` / `false` | -::: - -## FTP Settings { lang="en" } - -## FTP 设置 { lang="zh-CN" } +## FTP 设置 -::: en -Before understanding the FTP configuration options, it is important to first understand how the FTP protocol works. The FTP protocol uses **two TCP connections** for communication, which are referred to as the "**control flow**" and the "**data flow**." Port 21 is the default control flow port for the FTP protocol. The FTP server continuously listens on this port, waiting for connections from clients and responding accordingly. **The control flow only transmits client requests and server error messages, without transferring file contents or file listings.** In OpenList, the control flow port is determined by the `listen` parameter in the configuration file, with a default value of 5221. The client must be able to access this port on the server for the protocol to function properly. - -The establishment of the data flow can be done in two main ways, known as "active mode" and "passive mode": -::: - -::: zh-CN 在了解 FTP 的设置选项前需要先了解 FTP 协议的工作方式,FTP 协议使用**两个 TCP 连接**进行交互,它们分别被称为“**控制流**”和“**数据流**”,21 号端口是 FTP 协议的默认控制流端口号,FTP 服务器会持续监听这一端口号等待客户端的连接并给出响应,**控制流只传输客户端的请求指令和服务端的错误信息,不传输文件内容和文件列表**。在 OpenList 中,控制流端口号由配置文件内的`listen`参数确定,默认为 5221,客户端必须能够访问服务端的这一端口,协议才可以正常工作。 而数据流的建立主要有两种方式,被称为“主动模式”和“被动模式”: -::: - -::: en -In active mode, the client actively listens on a port and sends the port number to the server using the `PORT` command. The server then actively connects to the client and transmits files or a list of files. In this mode, the client must be directly accessible by the server. Therefore, in the context of widespread NAT, this mode generally only works when both the server and client are in the same subnet. - -```mermaid ---- -title: Active Mode ---- -sequenceDiagram - Client->>Server: Sends PORT command with client's IP address and data port number - Server->>Client: Actively connects to the client and transmits files or file list over the newly established connection (data stream) -``` - -::: -::: zh-CN 在主动模式下,客户端主动监听一个端口,并通过`PORT`指令将端口号发送给服务端,服务端主动连接客户端并传输文件或文件列表。在这种模式下,客户端需要能够被服务端直接访问,因此在当前 NAT 普及的情况下几乎只有服务端和客户端处在同一子网内时才能发挥作用。 ```mermaid @@ -104,25 +46,6 @@ sequenceDiagram 服务端->>客户端: 主动连接客户端,在新建立的连接(数据流)中传输文件或文件列表 ``` -::: - -::: en - -In passive mode, the client first sends the `PASV` or `EPSV` command in the control stream, requesting the server to listen on a new data port. The server then returns the port number of the new listening port to the client in the control stream. After the client establishes a connection with that port, data transfer begins. In this mode, the server does not actively initiate connections to the client, so it only needs to be outside of NAT. However, since the passive transfer port is not predetermined but determined before the connection is initiated, additional configuration is required when there are port mappings between the server and the client, or when only a subset of the server's ports are available for client connections in complex network environments. - -```mermaid ---- -title: Passive Mode ---- -sequenceDiagram - Client->>Server: Sends PASV or EPSV command in control stream - Server-->>Client: Server's public IP (only PASV) and passive transfer port number for data transfer - Client->>Server: Initiates TCP connection to the server's IP and port, then transfers files or file list over the newly established connection (data stream) -``` - -::: - -::: zh-CN 在被动模式下,客户端首先在控制流中发送`PASV`或`EPSV`命令,要求服务端新监听一个数据流端口,服务端在控制流中向客户端返回新监听的端口的端口号,待客户端与该端口建立连接以后再进行数据传输。在这种模式下,服务端不主动向客户端发起连接,因此只需要服务端处在 NAT 之外即可,但因为被动传输的传输端口不是预先确定,而是在连接发起前才确定的,因此在面对服务端和客户端之间存在端口映射,或服务端仅有一部分端口允许客户端连接的复杂网络环境时就需要额外配置。 ```mermaid @@ -135,22 +58,6 @@ sequenceDiagram 客户端->>服务端: 向服务端返回的IP和端口发起TCP连接,在新建立的连接(数据流)中传输文件或文件列表 ``` -::: - -::: en - -- FTP Server Public Network Address - - This is the IP address that the server sends to the client in the `PASV` command. If the server and the client are within the same subnet, the server's internal IP address can be used. Even if the server and the client are on the same machine, `127.0.0.1` can be used. However, if the server and client are not in the same subnet, the server's IP address that is accessible by the client must be specified. - - A domain name can also be specified. In this case, the default DNS will resolve the domain name to an IP address. However, since `PASV` only supports IPv4 addresses, if both an AAAA (IPv6) and an A (IPv4) record exist for the domain, the A record will be used. If only an AAAA record exists without an A record, the result is unknown. - - This field does not affect the `EPSV` command, but leaving this field invalid will cause the FTP server to fail to start. Therefore, if your FTP client only uses the `EPSV` command, you may consider keeping the default value `127.0.0.1`. - -::: - -::: zh-CN - - FTP 服务端公网地址 在`PASV`命令中服务端向客户端发送的 IP 地址,如果服务端和客户端在同一子网内,用服务端的内网 IP 地址也可以,甚至如果服务端和客户端是同一台主机,也可以使用`127.0.0.1`。但如果服务端和客户端不在同一子网内,则需要填写能够让客户端访问到的服务端 IP 地址。 @@ -159,71 +66,6 @@ sequenceDiagram 该字段不影响`EPSV`指令,但该字段无效会导致 FTP 服务器放弃启动,所以如果你的 FTP 客户端只使用 `EPSV`指令,可以考虑这里保留默认值`127.0.0.1`。 -::: - -::: en - -- FTP Passive Transfer Port Mapping - - This field consists of a series of "mapping groups" separated by commas (`,`) or newlines. The legal forms for a "mapping group" are as follows: - 1. `` - 2. `-` - 3. `:` - 4. `-::` - - All port numbers must be between 1024 and 65535 (inclusive), and the starting port number of a range must be less than the ending port number. - - For cases where this field is left blank, the server will choose any port between 1024 and 65535 for passive transfer and will not perform any mapping. - - Each "mapping group type 1" specifies a single port to be used for passive transfer, and no mapping will be performed for that port. - - Each "mapping group type 2" specifies a range of ports, and all ports in that range will be used for passive transfer without any mapping. - - Each "mapping group type 3" specifies a listening port to be used for passive transfer, and when the server selects this port, it will return the "response port number" to the client. - - "Mapping group type 4" requires that the two ranges before and after the colon `:` have equal lengths. Each "mapping group type 4" forms a one-to-one pairing of port numbers, where each pair is treated as a "mapping group type 3." - - The following are legal formats: - - `1024` - - `4001-5000,5001-6000:50001-510004000:65535` - - The following are illegal formats: - - `1023` (less than 1024) - - `65536` (greater than 65535) - - `4000, 5000` (space after the comma) - - `2000 - 3000 : 4000 - 5000` (spaces are illegal) - - `2000-2001:3000-3002` (unequal length of ranges) - - If the field is invalid, the server will choose any port between 1024 and 65535 for passive transfer without performing any mapping. - - The design of port mapping is intended to address the complexity of external port mapping. For example, if the server is running inside a Docker container and uses port 2000 for passive transfer, but Docker maps port 2000 inside the container to port 12000 on the host machine, you can achieve this mapping using the configuration `12000:2000`. - -```mermaid -sequenceDiagram - participant Client - participant Port Mapping - participant Server - Client->>Server: Send EPSV command - Server-->>Client: Return 12000 - Client->>Port Mapping: Connect to port 12000 - Port Mapping->>Server: Forward to port 2000 -``` - -If there are multiple layers of port mapping between the server and the client, only the port number closest to the client needs to be specified before the `:` symbol, and only the port number closest to the server needs to be specified after the `:` symbol. For example, in the following scenario, you can fill in `2000:5000`: - -```mermaid -sequenceDiagram - participant Client - participant NAT Traversal Service - participant Soft Router Port Forwarding - participant Docker Container Port Mapping - participant OpenList Process - Client->>NAT Traversal Service: 2000 - NAT Traversal Service->>Soft Router Port Forwarding: 3000 - Soft Router Port Forwarding->>Docker Container Port Mapping: 4000 - Docker Container Port Mapping->>OpenList Process: 5000 -``` - -::: - -::: zh-CN - - FTP 被动传输端口映射 该字段由一系列以英文逗号`,`或换行符隔开的“映射组”构成,“映射组”的合法形式有以下四种: @@ -284,46 +126,6 @@ sequenceDiagram Docker 容器端口映射->>OpenList 进程: 5000 ``` -::: - -::: en - -- FTP Proxy User-Agent Request Header - - Some storage drivers require a User-Agent request header when accessing the FTP server. You can simply use any fake value for this header. - -- Force FTP Connection to Use Explicit TLS - - Forces the use of the FTPS protocol, which only encrypts the data stream and not the control stream. - If the "Enable FTP Implicit TLS" option is enabled, this option will be ignored. - - If no valid TLS private key and certificate are provided, and this option is not enabled, the server will only accept the FTP protocol. - - If valid TLS private key and certificate are provided, but this option is not enabled, the server will accept both the FTP and FTPS protocols. - - If no valid TLS private key and certificate are provided, but this option is enabled, the FTP server will fail to start. - -- Enable FTP Implicit TLS - - Uses the FTPS protocol, which encrypts both the data stream and the control stream. This makes it incompatible with FTP and FTPS (explicit) protocols. - - When this option is enabled, the "Force FTP Connection to Use Explicit TLS" option will be ignored. - If no valid TLS private key and certificate are provided but this option is enabled, the FTP server will fail to start. - -- FTP TLS Private Key Path - - The path to the TLS private key file. Leaving it empty or providing an invalid path means TLS will not be enabled. - - Enabling TLS may require the client to access the server using a domain name, though the "FTP Server Public Address" can still be an IP address. - -- FTP TLS Certificate Path - - The path to the TLS certificate file. Leaving it empty or providing an invalid path means TLS will not be enabled. - -::: - -::: zh-CN - - FTP 代理 User-Agent 请求头 某些存储驱动在访问时需要用到 User-Agent 请求头,随便写一个假的就行。 @@ -358,26 +160,9 @@ sequenceDiagram TLS 证书文件路径,留空或无效表示不启用 TLS。 -::: - -## SFTP Configurations { lang="en" } - -## SFTP 配置文件\* { lang="zh-CN" } - -::: en - -| Field | Meaning | Example Value | -| ------ | ------------------------------ | --------------------------------------------------------- | -| enable | Whether enabled | `true` / `false` | -| listen | (Allowed access IP mask)\:port | `":5222"` (default) / `"0.0.0.0:22"` / `"127.0.0.1:2222"` | - -::: - -::: zh-CN +## SFTP 配置文件\* | 字段 | 含义 | 示例值 | | ------ | ----------------------- | ------------------------------------------------------- | | enable | 是否启用 | `true` / `false` | | listen | (允许访问的IP掩码):端口 | `":5222"`(默认) / `"0.0.0.0:22"` / `"127.0.0.1:2222"` | - -::: diff --git a/pages/guide/advanced/ipa.md b/pages/guide/advanced/ipa.md index cfc30b3ec..bb8a44b6f 100644 --- a/pages/guide/advanced/ipa.md +++ b/pages/guide/advanced/ipa.md @@ -1,28 +1,14 @@ --- -title: - en: IPA install - zh-CN: IPA 安装 categories: - guide - advanced top: 70 --- -## 1. IPA native installation { lang="en" } +# IPA 安装 -## 1. IPA原生安装 { lang="zh-CN" } +## 1. IPA原生安装 -::: en -You can directly upload the ipa file. -But for **ios>=16**, you need to name the ipa file: `original file name@bundle-identifier.ipa`, that is, you need to add `@bundle-identifier` on the original basis, in which `bundle-identifier` is the `CFBundleIdentifier` of Info.plist in the IPA package. Such as: - -- aszs@rn.notes.best.ipa - Recommended to use [ipa-renamer](https://github.com/Xhofe/ipa-renamer) to rename your ipa files automatically and quickly. -- If you are a mobile phone user, you can also rename the IPA file, but you must know the format mentioned above, but there is no mobile phone renaming software, please solve it yourself... - -::: - -::: zh-CN 直接上传 ipa 文件即可。 但是对于 **iOS 16 以上的** 设备,你需要将 ipa 文件命名为:`原文件名@bundle-identifier.ipa`,即需要在原来的基础上加入`@bundle-identifier`,其中的 `bundle-identifier` 即 IPA 包中 `Info.plist` 的 `CFBundleIdentifier`。如: @@ -30,31 +16,7 @@ But for **ios>=16**, you need to name the ipa file: `original file name@bundle-i 推荐你使用 [ipa-renamer](https://github.com/Xhofe/ipa-renamer) 自动快速地重命名你的 IPA 文件。 - 如果你是手机用户也可以给IPA文件改名,但是你要会上述提到的格式才行,但是不提供手机改名软件请你自己解决... -::: - -### Tips { lang="en" } - -### Tips { lang="zh-CN" } - -::: en - -- **.ipa installation requires https and a valid certificate** , not just a package can be installed without a signature - - How to change the name of the computer version - - - - - How to change the name of the IOS 16 mobile phone - - - -**Installation Demo**: Deploy **HTTPS** and **IPA files** are signed prerequisites, if **IOS16 rename by yourself** If you can’t install it yourself, please check the problem yourself - -- **Installation demo environment**: IOS15.4.1 iPhone12PM, personal certificate, HTTPS - - - -::: -::: zh-CN +### Tips - **.ipa安装 需要 https 和 有效的证书**,不是随便一个包上传上去没签名就能安装的 - 爱思助手签名的无法使用在线安装,只能使用爱思助手安装(经群友测试) @@ -72,42 +34,7 @@ But for **ios>=16**, you need to name the ipa file: `original file name@bundle-i -::: - -## 2. TrollStore install { lang="en" } - -## 2. TrollStore(巨魔)安装 { lang="zh-CN" } - -::: en -**Requires OpenList version ≥ 3.12.2 and above**,Devices that support the installation of TrollStore (jailbroken devices are not explained here) - -For more devices, please see the table below. The content of the chart comes from [**TrollStore**](https://github.com/opa334/TrollStore) - -Chart information update is uncertain, you can go to view https://ios.cfw.guide/installing-trollstore - -| From | To | **arm64 (A8)** | arm64 (A9-A11) | arm64e (A12-A17/M1-M2) | -| :--------------------: | :--------------------: | :--------------------------------------------------------------------------------: | :----------------------------------------------------------------------------: | :----------------------------------------------------------------------------: | -| 14.0 beta 1and earlier | 14.0 beta 1and earlier | Unsupported | Unsupported | Unsupported | -| 14.0 beta 2 | 14.8.1 | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | -| 15.0 | 15.5 beta 4 | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | -| 15.5 | 15.5 | [TrollInstallerMDC](https://ios.cfw.guide/installing-trollstore-trollinstallermdc) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | -| 15.6 beta 1 | 15.6 beta 3 | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | -| 15.6 beta 4 | 15.6.1 | [TrollInstallerMDC](https://ios.cfw.guide/installing-trollstore-trollinstallermdc) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | -| 15.7 | 15.7.1 | [TrollInstallerMDC](https://ios.cfw.guide/installing-trollstore-trollinstallermdc) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | -| 15.7.2 | 15.8.2 | [TrollMisaka](https://ios.cfw.guide/installing-trollstore-trollmisaka) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | -| 16.0 beta 1 | 16.0 beta 3 | Not Applicable | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollHelperOTA](https://ios.cfw.guide/installing-trollstore-trollhelperota) | -| 16.0 beta 4 | 16.6.1 | Not Applicable | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | -| 16.7 RC | 16.7 RC | Not Applicable | [TrollHelper](https://ios.cfw.guide/installing-trollstore-trollhelper) | **Coming Soon** | -| 16.7 | 16.7.8 | Not Applicable | Unsupported | Unsupported | -| 17.0 beta 1 | 17.0 beta 4 | Not Applicable | [TrollInstallerX](https://ios.cfw.guide/installing-trollstore-trollinstallerx) | **Coming Soon** | -| 17.0 beta 5 | 17.0 | Not Applicable | [TrollHelper](https://ios.cfw.guide/installing-trollstore-trollhelper) | **Coming Soon** | -| 17.0.1 and later | 17.0.1 and later | Not Applicable | Unsupported | Unsupported | - - IPA Resources :gift: - -::: - -::: zh-CN +## 2. TrollStore(巨魔)安装 **需要OpenList版本 ≥ 3.12.2 及以上**,支持安装TrollStore(巨魔)的设备(不含越狱设备) @@ -135,63 +62,13 @@ Chart information update is uncertain, you can go to view  IPA Resources :gift: -::: - -### Installation example { lang="en" } - -### 安装示例 { lang="zh-CN" } - -::: en - -If you want to use the installation is very simple, click the **TrollStore** button to install. - -![gif](https://pic.rmb.bdstatic.com/bjh/ff1e47ebc1efe5a907dbfffabf4d5f67.gif) - -::: -::: zh-CN +### 安装示例 如果你要使用安装很简单,点击 **TrollStore(巨魔)** 按钮即可安装。 ![gif](https://pic.rmb.bdstatic.com/bjh/ff1e47ebc1efe5a907dbfffabf4d5f67.gif) -::: - -### Other instructions { lang="en" } - -### 其他说明 { lang="zh-CN" } - -::: en - -1. If you click the **TrollStore** button to open Apple's magnifying glass, it is because of the following reasons - - Your `TrollStore` version number is less than version 1.3+, it is recommended to update to the latest version directly - - Your URL scheme is not enabled. If you enable it, remember to click the first option to log out immediately **(Rebuild Now)** - - ![TrollStore](/img/advanced/TrollStore.jpg) -2. What is the wake-up format of the TrollStore URL? - > `apple-magnifier://install?url=` -3. Is there any benefit to this installation? - - See the flow chart below for explanation - -```mermaid ---- -title: Is there any benefit to this installation? ---- -flowchart TB - A-->B - subgraph A[original process] - C[First download the application from OpenList]-->D[find the downloaded software] - D-->E[click on the software and select share in the upper right corner] - E-->F[find the troll icon] - F-->G[jump into the troll software to install] - G-->H[install and delete the software] - end - subgraph B[troll process] - I[Click the OpenList troll install button and click the pop-up window prompt]-->J[enter the troll app and wait for download] - J-->K[after downloading, click the install button] - end -``` - -::: -::: zh-CN +### 其他说明 1. 如果你点击 **TrollStore(巨魔)** 按钮后打开的是 Apple的放大镜是因为如下原因 - 你的 `TrollStore` 版本号小于1.3+版本,建议直接更新到最新版 @@ -223,5 +100,3 @@ flowchart TB G==>J H===|使用巨魔不用手动删除|K ``` - -::: diff --git a/pages/guide/advanced/mcp.md b/pages/guide/advanced/mcp.md index 7e3850c31..453ac66b7 100644 --- a/pages/guide/advanced/mcp.md +++ b/pages/guide/advanced/mcp.md @@ -1,25 +1,12 @@ --- -title: - en: MCP - zh-CN: MCP categories: - guide - advanced top: 20 --- -::: en -[MCP (Model Context Protocol)](https://modelcontextprotocol.io) is an open protocol that standardizes how applications provide context and tools to Large Language Models (LLMs). OpenList implements MCP as a **Streamable HTTP** transport endpoint, enabling AI agents and MCP clients to interact with file storage. +# MCP -With the OpenList MCP endpoint, you can: - -- List files and directories -- Get file or directory details -- Obtain download links - -::: - -::: zh-CN [MCP (Model Context Protocol)](https://modelcontextprotocol.io) 是一种开放协议,它标准化了应用程序向大型语言模型(LLM)提供上下文和工具的方式。OpenList 将 MCP 作为 **Streamable HTTP** 传输端点实现,使 AI 智能体和 MCP 客户端能够与文件存储进行交互。 通过 OpenList MCP 端点,您可以: @@ -28,29 +15,7 @@ With the OpenList MCP endpoint, you can: - 获取文件或目录详情 - 获取下载链接 -::: - -## How to Enable { lang="en" } - -## 如何启用 { lang="zh-CN" } - -::: en - -1. Open the [Configuration File](../../configuration/configuration.md#mcp) -2. Set `mcp.enable` to `true`: - -```json{2} - "mcp": { - "enable": true - } -``` - -3. Restart OpenList for the change to take effect -4. The MCP endpoint is now available at `http[s]://your-domain:port/mcp` - -::: - -::: zh-CN +## 如何启用 1. 打开 [配置文件](../../configuration/configuration.md#mcp) 2. 将 `mcp.enable` 设置为 `true`: @@ -64,32 +29,8 @@ With the OpenList MCP endpoint, you can: 3. 重启 OpenList 使配置生效 4. MCP 端点现在可以通过 `http[s]://your-domain:port/mcp` 访问 -::: - -## MCP Client Configuration { lang="en" } - -## MCP 客户端配置 { lang="zh-CN" } - -::: en -To connect an MCP client (such as an AI coding agent that supports MCP) to OpenList, use the following configuration: - -**Transport type**: Streamable HTTP - -**URL**: `http[s]://your-domain:port/mcp` - -**Authentication**: Token Authentication. The token is your OpenList login token — include it directly in the `Authorization` header of each request (e.g. `Authorization: `). Do **NOT** add a `Bearer` prefix. Obtain the token from your OpenList account settings page or via the API. - -::: warning -OpenList's authentication middleware reads the `Authorization` header value as-is. Adding a `Bearer ` prefix will result in authentication failure. Use `Authorization: ` only. -::: - -::: en -**Protocol version**: `2025-11-25` (also compatible with `2025-06-18`) +## MCP 客户端配置 -**Session handling**: A session is created on `initialize` and identified via the `MCP-Session-Id` response header. You must include this header in subsequent requests. -::: - -::: zh-CN 要将 MCP 客户端(例如支持 MCP 的 AI 编码助手)连接到 OpenList,请使用以下配置: **传输类型**:Streamable HTTP @@ -102,36 +43,14 @@ OpenList's authentication middleware reads the `Authorization` header value as-i OpenList 的认证中间件直接对比 `Authorization` 头的原始值。如果添加 `Bearer ` 前缀会导致认证失败,请仅使用 `Authorization: `。 ::: -::: zh-CN - **协议版本**:`2025-11-25`(同时兼容 `2025-06-18`) **会话管理**:在 `initialize` 时创建会话,并通过 `MCP-Session-Id` 响应头标识。后续请求必须包含此标头。 -::: -## Available Tools { lang="en" } - -## 可用工具 { lang="zh-CN" } +## 可用工具 ### `openlist.fs.list` -::: en -List files and directories under a mount path that the current user can access. - -**Parameters**: - -| Parameter | Type | Required | Description | -| ---------- | --------- | -------- | ---------------------------------------------- | -| `path` | `string` | Yes | Mount path to list, e.g. `"/"` or `"/movies"` | -| `refresh` | `boolean` | No | Refresh the directory listing before returning | -| `password` | `string` | No | Optional password for protected paths | -| `page` | `integer` | No | 1-based page number (default: 1) | -| `per_page` | `integer` | No | Page size (default: all items) | - -**Response**: Returns file list with name, size, type, modification time, thumbnail, hash info, and storage details. -::: - -::: zh-CN 列出当前用户可访问的挂载路径下的文件和目录。 **参数**: @@ -145,24 +64,9 @@ List files and directories under a mount path that the current user can access. | `per_page` | `integer` | 否 | 每页大小(默认:全部) | **响应**:返回文件列表,包含名称、大小、类型、修改时间、缩略图、哈希信息和存储详情。 -::: ### `openlist.fs.get` -::: en -Get file or directory details for a mount path that the current user can access. - -**Parameters**: - -| Parameter | Type | Required | Description | -| ---------- | -------- | -------- | ------------------------------------------------ | -| `path` | `string` | Yes | Mount path to inspect, e.g. `"/movies/demo.mp4"` | -| `password` | `string` | No | Optional password for protected paths | - -**Response**: Returns file details including name, size, type, modification time, raw URL, readme, header, provider, and related files at the same level. -::: - -::: zh-CN 获取当前用户可访问的挂载路径下的文件或目录详情。 **参数**: @@ -173,25 +77,9 @@ Get file or directory details for a mount path that the current user can access. | `password` | `string` | 否 | 受保护路径的密码 | **响应**:返回文件详情,包括名称、大小、类型、修改时间、原始 URL、README、Header、提供商及同级相关文件。 -::: ### `openlist.fs.link` -::: en -Return usable link information for a file path that the current user can access. - -**Parameters**: - -| Parameter | Type | Required | Description | -| ---------- | -------- | -------- | ----------------------------------------------- | -| `path` | `string` | Yes | File mount path, e.g. `"/movies/demo.mp4"` | -| `password` | `string` | No | Optional password for protected paths | -| `type` | `string` | No | Optional link type forwarded to storage drivers | - -**Response**: Returns link information including direct URL, proxy URL, download URL, HTTP headers, content length, concurrency, and part size (for multi-part downloads). -::: - -::: zh-CN 返回当前用户可访问的文件路径的可用的链接信息。 **参数**: @@ -203,26 +91,10 @@ Return usable link information for a file path that the current user can access. | `type` | `string` | 否 | 传递给存储驱动的可选链接类型 | **响应**:返回链接信息,包括直链 URL、代理 URL、下载 URL、HTTP 头、内容长度、并发数和分片大小(用于多部分下载)。 -::: - -## Protocol Details { lang="en" } - -## 协议细节 { lang="zh-CN" } - -### Transport { lang="en" } -### 传输方式 { lang="zh-CN" } +## 协议细节 -::: en -OpenList implements the MCP **Streamable HTTP** transport. The endpoint accepts: - -- `POST /mcp` — Main endpoint for all MCP JSON-RPC requests -- `GET /mcp` — Returns `405 Method Not Allowed` with `Allow: POST, DELETE` (used for CORS preflight validation) -- `DELETE /mcp` — Terminates an active session (requires `MCP-Session-Id` header) - -::: - -::: zh-CN +### 传输方式 OpenList 实现了 MCP **Streamable HTTP** 传输。端点接受: @@ -230,39 +102,11 @@ OpenList 实现了 MCP **Streamable HTTP** 传输。端点接受: - `GET /mcp` — 返回 `405 Method Not Allowed` 及 `Allow: POST, DELETE`(用于 CORS 预检验证) - `DELETE /mcp` — 终止活跃会话(需要 `MCP-Session-Id` 头) -::: - -### Authentication {{ lang="en" }} - -### 认证 {{ lang="zh-CN" }} - -::: en - -The MCP endpoint reuses OpenList's authentication middleware. Requests must include a valid `Authorization` header with a valid user token. The session is bound to the authenticated user — a user cannot use sessions belonging to other users. - -::: - -::: zh-CN +### 认证 MCP 端点复用 OpenList 的认证中间件。请求必须在 `Authorization` 头中包含有效的用户令牌。会话与认证用户绑定——用户不能使用属于其他用户的会话。 -::: - -### Session Lifecycle {{ lang="en" }} - -### 会话生命周期 {{ lang="zh-CN" }} - -::: en - -1. **Initialize**: Client sends `initialize` request. Server returns a `MCP-Session-Id` response header. -2. **Notify Initialized**: Client sends `notifications/initialized` to mark the session as ready. -3. **Tool Calls**: Client sends `tools/list` and `tools/call` requests with the `MCP-Session-Id` header. -4. **Ping**: Client may send `ping` to keep the session alive. -5. **Cleanup**: Sessions expire after 30 minutes of inactivity. Global maximum: 128 sessions per server, 16 sessions per user. The least recently used sessions are evicted first. - -::: - -::: zh-CN +### 会话生命周期 1. **初始化**:客户端发送 `initialize` 请求。服务器返回 `MCP-Session-Id` 响应头。 2. **通知已初始化**:客户端发送 `notifications/initialized` 将会话标记为就绪。 @@ -270,49 +114,13 @@ MCP 端点复用 OpenList 的认证中间件。请求必须在 `Authorization` 4. **心跳**:客户端可发送 `ping` 保持会话活跃。 5. **清理**:会话在 30 分钟无活动后过期。全局上限:每台服务器 128 个会话,每个用户 16 个会话。最近最少使用的会话会被优先淘汰。 -::: - -### Protocol Version {{ lang="en" }} - -### 协议版本 {{ lang="zh-CN" }} - -::: en - -Supported protocol versions: `2025-11-25` (default), `2025-06-18`. - -The server returns its protocol version during `initialize` negotiation. Subsequent requests must carry a compatible `MCP-Protocol-Version` header matching the negotiated version. - -::: - -::: zh-CN +### 协议版本 支持的协议版本:`2025-11-25`(默认)、`2025-06-18`。 服务器在 `initialize` 协商期间返回其协议版本。后续请求必须携带与协商版本匹配的 `MCP-Protocol-Version` 头。 -::: - -### Error Codes {{ lang="en" }} - -### 错误码 {{ lang="zh-CN" }} - -::: en - -| Code | Meaning | -| ------ | ---------------------------------- | -| -32700 | Parse error | -| -32600 | Invalid request | -| -32601 | Method not found | -| -32602 | Invalid params | -| -32603 | Internal error | -| -32000 | Bad request (missing headers, etc) | -| -32001 | Session/user not found | -| -32002 | Session not initialized | -| -32003 | Permission error | - -::: - -::: zh-CN +### 错误码 | 错误码 | 含义 | | ------ | -------------------- | @@ -326,36 +134,8 @@ The server returns its protocol version during `initialize` negotiation. Subsequ | -32002 | 会话未初始化 | | -32003 | 权限错误 | -::: - -## Example: Using with AI Coding Agents { lang="en" } - -## 示例:与 AI 编码助手一起使用 { lang="zh-CN" } +## 示例:与 AI 编码助手一起使用 -::: en -Many AI coding assistants support the MCP protocol and can be configured to use OpenList as a file system tool. Below is an example configuration (e.g., for VS Code or Claude Desktop): - -```json -{ - "mcpServers": { - "openlist": { - "type": "http", - "url": "https://your-domain:port/mcp", - "headers": { - "Authorization": "YOUR_OPENLIST_TOKEN" - } - } - } -} -``` - -Replace `YOUR_OPENLIST_TOKEN` with your actual OpenList authentication token, which can be obtained from the OpenList management panel. - -> **Warning**: Do **NOT** prefix the token with `Bearer `. The `Authorization` header value must be the token itself, nothing else. - -::: - -::: zh-CN 许多 AI 编码助手支持 MCP 协议,可以配置为使用 OpenList 作为文件系统工具。以下是一个示例配置(例如用于 VS Code 或 Claude Desktop): ```json @@ -375,5 +155,3 @@ Replace `YOUR_OPENLIST_TOKEN` with your actual OpenList authentication token, wh 将 `YOUR_OPENLIST_TOKEN` 替换为你的 OpenList 认证令牌,可从 OpenList 管理面板获取。 > **Warning**:**不要**在令牌前加 `Bearer ` 前缀。`Authorization` 头的值必须直接是令牌本身,不要加任何前缀。 - -::: diff --git a/pages/guide/advanced/meta.md b/pages/guide/advanced/meta.md index 989c85f4d..5c1909e4f 100644 --- a/pages/guide/advanced/meta.md +++ b/pages/guide/advanced/meta.md @@ -1,52 +1,20 @@ --- -title: - en: Meta - zh-CN: 元信息 categories: - guide - advanced top: 80 --- -::: en -Most settings in meta information apply to `All Roles`. The `Read Users` and `Write Users` fields are exceptions — they allow you to restrict access to specific users. -::: -::: zh-CN -元信息中的大多数设置对`所有角色`生效。`可读用户`和`可写用户`字段是例外——它们允许你将访问权限限制到特定用户。 -::: +# 元信息 -## Path { lang="en" } +元信息中的大多数设置对`所有角色`生效。`可读用户`和`可写用户`字段是例外——它们允许你将访问权限限制到特定用户。 -## 路径 { lang="zh-CN" } +## 路径 -::: en -The path for this meta to take effect. -::: -::: zh-CN 此元信息生效的路径。 -::: - -## Password { lang="en" } - -## 密码 { lang="zh-CN" } -::: en -Password required to access this path. +## 密码 -- Meta information password will not take effect when accessed using `WebDav` - -::: danger Notes -If you want the password to be effective in subfolders, please check `Apply to sub folder` after the password. Do not check `Write` and then check `Apply to sub folder` - -The correct check example is shown below. If you configure the options correctly, if you check the wrong option `Write` and then check `Apply to sub folder`, it will cause anyone to have permission to write dangerous operations - -![](/img/advanced/meta/password_b.png#light) - -![](/img/advanced/meta/password_h.png#dark) - -::: - -::: zh-CN 访问此路径需要密码 - 元信息密码在使用`WebDav`访问时不会生效 @@ -62,106 +30,39 @@ The correct check example is shown below. If you configure the options correctly ::: -## Read Users { lang="en" } - -## 可读用户 { lang="zh-CN" } - -::: en -A whitelist of users allowed to read this path. If the list is non-empty, only the selected users can access the path; all other users will be denied access. - -- Users are selected by name in the management interface -- Check `Apply to sub folder` to also restrict sub-directories +## 可读用户 -::: -::: zh-CN 允许读取此路径的用户白名单。如果列表非空,则只有选中的用户才能访问该路径,其他所有用户将被拒绝访问。 - 在管理界面中通过用户名选择用户 - 勾选`应用到子文件夹`可同时限制子目录 -::: - -## Write Users { lang="en" } - -## 可写用户 { lang="zh-CN" } +## 可写用户 -::: en -A whitelist of users allowed to write to this path. If the list is non-empty, only the selected users can perform write operations (upload, create, rename, move, delete) on this path. - -- Users are selected by name in the management interface -- Check `Apply to sub folder` to also restrict sub-directories - -::: -::: zh-CN 允许写入此路径的用户白名单。如果列表非空,则只有选中的用户才能对该路径执行写入操作(上传、新建、重命名、移动、删除)。 - 在管理界面中通过用户名选择用户 - 勾选`应用到子文件夹`可同时限制子目录 -::: +## 开放写入 -## Write Content Bypass { lang="en" } - -## 开放写入 { lang="zh-CN" } - -::: en -Allow any user to make directory, create new file and upload files, bypassing user-level write permission checks. -::: -::: zh-CN 允许任何用户新建目录、新文件和上传文件,绕过用户级别的写入权限检查。 -::: - -## Hide { lang="en" } - -## 隐藏 { lang="zh-CN" } - -::: en -The objects to hide of this path, One regular expression (in `Golang`) per line -- Meta information hiding can take effect when accessed using `WebDav` +## 隐藏 -::: -::: zh-CN 此路径要隐藏的对象,每行一个正则表达式(在 `Golang` 中) - 元信息隐藏在使用`WebDav`访问时可以生效 -::: - -## Readme { lang="en" } - -## 说明 { lang="zh-CN" } +## 说明 -::: en -The Readme to render while enter this path, support markdown content or markdown link. - -- Show at bottom of list -- The automatically rendered file name is: **`readme.md`** - -::: -::: zh-CN 进入该路径时渲染的自述文件,支持 Markdown 内容或 Markdown 链接。 - 在列表底部显示 - 自动渲染的文件名为:**`readme.md`** -::: - -## Header { lang="en" } - -## 顶部说明 { lang="zh-CN" } - -::: en -The Readme to render while enter this path, support markdown content or markdown link. +## 顶部说明 -- Show at top of list -- The automatically rendered file name is: **`top.md`** - - Files can not be displayed in the list, for example `readme.md` is not displayed in the list - - Backstage --> Settings --> Global --> Hidden files --> Add newline `/\/top.md/i` - -::: - -::: zh-CN 进入该路径时渲染的自述文件,支持 Markdown 内容或 Markdown 链接。 - 在列表顶部显示 @@ -169,36 +70,14 @@ The Readme to render while enter this path, support markdown content or markdown - 可以在列表中不显示文件,例如`readme.md`不在列表显示 - 后台 --> 设置 --> 全局 --> 隐藏文件 --> 换行添加 `/\/top.md/i` -::: - -## Apply to sub folder { lang="en" } - -## 应用到子文件夹 { lang="zh-CN" } +## 应用到子文件夹 -::: en -Apply this meta to sub folder of specific path -::: -::: zh-CN 将此元信息应用于特定路径的子文件夹 -::: -## :warning: Tips { lang="en" } +## :warning: Tips -## :warning: Tips { lang="zh-CN" } - -::: en - -Regarding hidden, users without permissions can search for hidden folders/files, solutions: - -:white_check_mark: If you want to hide the folder in a folder, create a new Yuan information alone, and select the folder we want to hide,,Hidden if you want to hide everything, write directly`.*` - -:x: You cannot directly fill in the meta information of the root directory `/`, and then fill in the folder we want to hide, the error case [View details](https://github.com/alist-org/alist/issues/4494) > ![](/img/advanced/hide-tips.png) -::: - -::: zh-CN 关于隐藏,没有权限的用户可以搜索到隐藏的文件夹/文件,解决方案: :white_check_mark:如果你要隐藏某个文件夹內的文件夹,要单独新建一条元信息,路径选择我们要隐藏的文件夹,隐藏如果你要隐藏所有直接写 `.*`即可 :x: 不可以直接在选择根目录`/`的元信息內填写,然后隐藏位置填写我们要隐藏的文件夹,错误案例[查看详情](https://github.com/alist-org/alist/issues/4494) > ![](/img/advanced/hide-tips.png) -::: diff --git a/pages/guide/advanced/offline-download.md b/pages/guide/advanced/offline-download.md index bdc6c1c9e..a395691ab 100644 --- a/pages/guide/advanced/offline-download.md +++ b/pages/guide/advanced/offline-download.md @@ -1,34 +1,14 @@ --- -title: - en: Offline download - zh-CN: 离线下载 categories: - guide - advanced top: 50 --- -## Please read this tip before use { lang="en" } +# 离线下载 -## 使用前请阅读此提示 { lang="zh-CN" } +## 使用前请阅读此提示 -::: en -::: warning Please read the following tips carefully -There are two kinds of software with offline download function - -The two usages are the same, and the software configuration is set in advance (see the following instructions for specific configuration), - -Then go back to the front-end interface, **find the folder you want to upload offline files to, and you can manually enter the folder** - -Then find the offline download button in the lower right corner, and choose to use Aria2 download or qBittorrent download (>v 3.11.0) - -If you use **docker**, please map the following two default paths by yourself. (mapped to the same directory on the host machine) - -- **/opt/openlist/data/temp/aria2** -- **/opt/openlist/data/temp/qBittorrent** - -::: -::: zh-CN ::: warning 请仔细阅读以下提示 有两款离线下载功能软件 @@ -45,22 +25,6 @@ If you use **docker**, please map the following two default paths by yourself. ( ::: -::: en -::: danger OpenList ≥ 3.42.0 -OpenList Version ≥ 3.42.0 View [Allow all cloud disks to call other cloud disks' offline download tools](../../configuration/other.md) - -- ≥ 3.42.0 If you want to use offline download, you need to configure a temporary folder in the background before you can use it (otherwise it will only show **SimpleHttp**) -- manage ==> settings ==> other ( **https://example.com/@manage/settings/other** ) - - If you want to [use/configure] PikPak for offline downloading - 1. Mount PikPak storage - 2. Set Pikpak temp dir in the background - 3. Select any folder of this account as a temporary directory - - If multiple PikPaks are mounted and you want to use that account for offline downloading, then select the directory of that account as the temporary directory. - -**The following applies only to configuration instructions** - -::: -::: zh-CN ::: danger OpenList ≥ 3.42.0 OpenList版本 ≥ 3.42.0 的查看 [允许所有云盘调用其他云盘的离线下载工具](../../configuration/other.md) @@ -78,15 +42,8 @@ OpenList版本 ≥ 3.42.0 的查看 [允许所有云盘调用其他云盘的离 ### Aria2 -::: en -[**Click to view instructions for use**](../../configuration/other.md) - -::: -::: zh-CN [**点击查看使用说明**](../../configuration/other.md) -::: - ### qBittorrent #### Docker Compose @@ -134,34 +91,6 @@ networks: #### Windows -::: en -(Here we take the Windows side as an example, I don’t know if there is one on the Linux side) -First of all, we need to configure the default values on the client side of **`qBittorrent`** -According to [source code](https://github.com/alist-org/alist/blob/main/internal/bootstrap/data/setting.go#L159-L162), we know that the default values are admin:adminadmin@localhost:8080/ - -- **ip**: localhost -- **port**: 8080 -- **user**: admin -- **password**: adminadmin - -1. We fill in the above parameters into the **`qBittorrent`** client, after configuration, we go to the OpenList front end to download offline (**method refer to the description at the beginning**) - - If you are prompted after submitting the offline link: **Qbittorrent not ready**, try restarting both OpenList and qBittorrent -2. Default value configuration view address: (The link may also change position based on subsequent optimization) - - **https://github.com/OpenListGo/alist/blob/main/internal/offline_download/qbit/qbit.go#L28** - - `{ *** Value: "http://admin:adminadmin@localhost:8080/", Type: conf.TypeString, *** } ` - -3. Use **`qBittorrent`** to offline `.torrent` type files - - Although you cannot directly add offline `.torrent` type files, you can save the country with a curve Reference: [View method](https://github.com/alist-org/alist/issues/2459#issuecomment-1354870010) - -4. You can configure it yourself not to delete after the download is complete, but to do seeding, the default is `0`, and it will be deleted immediately after uploading - - Modification location: **OpenList background** --> **Settings** --> **Qbittorrent seedtime** option, set the time you need to configure, the unit is `minute`, after the set seeding time is reached, it will automatically delete - -5. **We can also customize, instead of using the default presets** - - Modification location: **OpenList Manage** --> **Settings** --> **Qbittorrent url** option, just follow the modification - ![Offline download](/img/advanced/offline-download.png) - -::: -::: zh-CN (这里以Windows端举例,Linux端不清楚有没有) 首先我们先要在 **`qBittorrent`** 客户端配置好预设值 根据[源码](https://github.com/OpenListTeam/OpenList/blob/main/internal/bootstrap/data/setting.go#L159-L162)我们知道预设值分别是 admin:adminadmin@localhost:8080/ @@ -187,32 +116,7 @@ According to [source code](https://github.com/alist-org/alist/blob/main/internal - 修改位置:**OpenList后台** --> **设置** --> **Qbittorrent url** 选项,照着修改即可 ![Offline download](/img/advanced/offline-download.png) -::: - -#### Precautions { lang="en" } - -#### 注意事项 { lang="zh-CN" } - -::: en - -1. Appeared in the background qBittorrent task: **`torrent parse timeout`**, parsing timed out -2. The torrent can be parsed, but **`The system cannot find the path specified.`** appears when uploading. - - The above two methods are most likely caused by the qBittorrent software itself, probably because of the green version and the modified version that have undergone some functional castration modifications. It is recommended to go to the official website to download and install it to restore it (the conclusion drawn by myself after stepping on the pit) - - **qBittorrent** official website: **https://www.qbittorrent.org/** - - **qBittorrent-Enhanced-Edition** GitHub: **https://github.com/c0re100/qBittorrent-Enhanced-Edition** - -3. Because the `v2.8.3` API is used, the minimum version of qBittorrent should be `4.4.0beta2` or `4.3.8` official version -4. If **qBittorrent** sets the ip where alist is located to be exempt from authentication, you can directly omit the user name and password before `@` (configured on the **qBittorrent** client side) - - 3 and 4 are not mentioned in the video. - **qBittorrent** Video Tutorials - - - - **If the video fails, you can watch it here: https://b23.tv/J34qDiG** - -::: -::: zh-CN +#### 注意事项 1. 在后台qBittorrent任务中出现:**`torrent parse timeout`**,解析超时 2. 可以解析种子下载,但是在上传的时候出现 **`The system cannot find the path specified.`** 找不到路径 @@ -230,19 +134,10 @@ According to [source code](https://github.com/alist-org/alist/blob/main/internal **视频若失效可以前往观看:https://b23.tv/J34qDiG** -::: - ### SimpleHttp -::: en -Subsequent supplement - -::: -::: zh-CN 后续补充 -::: - ### Transmission #### Docker Compose @@ -289,24 +184,8 @@ networks: openlist: ``` -### 115 Cloud、PikPak、Thunder { lang="en" } - -### 115、PikPak、迅雷 { lang="zh-CN" } +### 115、PikPak、迅雷 -::: en -Versions v3.42.0 and above support calling the offline download function in OpenList - -After setting other configuration temporary directory options in the background, select the offline download option in the lower right corner of the front-end page to select the corresponding driver offline download function - -- Support:`magnet`, `http`, `ed2k` links - - PikPak: Also supports: X, TikTok, Facebook, TG URL links -- Some tips for using 115 offline downloads: - 1. Out of sync problems may occur (manual refresh in the lower right corner ) - 2. Currently, when the download is successful, completed tasks in the offline list are deleted. - 3. 115 Task URLs that are already in the offline list cannot be added again. - -::: -::: zh-CN v3.42.0 及以上版本支持在OpenList调用离线下载功能 在后台设置其他配置临时目录选项后,前端页面右下角选择 离线下载选项选择对应的驱动离线下载功能 @@ -317,5 +196,3 @@ v3.42.0 及以上版本支持在OpenList调用离线下载功能 1. 可能会发生不同步的问题(手动右下角刷新 ) 2. 目前当下载成功后,删除离线列表中完成的任务 3. 115已经在离线列表中的任务url不能再次添加 - -::: diff --git a/pages/guide/advanced/rapid_upload.md b/pages/guide/advanced/rapid_upload.md index 0ff56215c..c82f67207 100644 --- a/pages/guide/advanced/rapid_upload.md +++ b/pages/guide/advanced/rapid_upload.md @@ -1,64 +1,33 @@ --- -title: - en: Rapid upload - zh-CN: 秒传 categories: - guide - advanced top: 130 --- -## Introduce { lang="en" } +# 秒传 -## 介绍 { lang="zh-CN" } +## 介绍 -::: en -**Rapid upload** means that files between two cloud disks can be uploaded to each other quickly without waiting. Here is an introduction to what can be mutually Rapid uploaded -**There is a premise that the files required for Rapid upload in network disk A are already available in network disk B, so that Rapid upload can be performed, otherwise it will be uploaded normally** -::: en -::: tip -**The left side** is the source Network Disk、**The top side** is the target Network Disk -::: -::: en -| | Aliyun | 115 | Baidu(5) | 189 | Xunlei | PikPak | GoogleDrive | OneDrive | localhost | -| :---------: | :----------------: | :----------------: | :----------------: | :----------------: | :-------------: | :----------------: | :-------------: | :-------------: | :-------------: | -| localhost | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | -| Aliyun | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| 115 | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| Baidu | :x: | :x: | :question: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| 189 | :x: | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| Xunlei | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | -| PikPak | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | -| GoogleDrive | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| OneDrive | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :no_entry_sign: | - -1. The **Aliyun** mentioned here refers to [**aliyundriveOpen**](../../guide/drivers/aliyundrive_open.md) -2. **OneDrive** has not been tested because there is no account. If the test is valid and there are no problems, feedback is welcome. -3. **Aliyun** and **189**: Both need to turn on the Rapid upload option in the storage configuration, otherwise Rapid upload cannot be performed -4. **GoogleDrive** files can be transferred to **189** in seconds and **189** can transfer files to **189** in seconds. There may be some problems and are not very stable. Need to pay attention to observation -5. **Baidu Net Disk** can only be stored from local storage Rapid Upload to the net disk. If you use cross-storage to Copy Rapid Upload, you can only download the file first and then perform Rapid Upload. - -::: -::: zh-CN **秒传** 就是两个云盘之间的文件可以快速相互上传,不用等待,下面介绍一下都有哪些可以相互秒传。 有个前提,就是A网盘需要秒传的文件,在B网盘已经有了,才可以进行秒传,否则就是正常上传。 -::: zh-CN + ::: tip **左侧**为来源网盘、**顶部**为目标网盘。 ::: -::: zh-CN -| | 阿里云盘 | 115 | 百度云盘(5) | 天翼云盘 | 迅雷云盘 | PikPak | 谷歌云盘 | OneDrive | 本地存储 | -| :------: | :----------------: | :----------------: | :----------------: | :----------------: | :-------------: | :----------------: | :-------------: | :-------------: | :-------------: | -| 本地存储 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | -| 阿里云盘 | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| 115 | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| 百度云盘 | :x: | :x: | :question: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| 天翼云盘 | :x: | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| 迅雷云盘 | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | -| PikPak | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | -| 谷歌云盘 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | -| OneDrive | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :no_entry_sign: | + +| | 阿里云盘 | 115 | 百度云盘(5) | 天翼云盘 | 迅雷云盘 | PikPak | 谷歌云盘 | OneDrive | 本地存储 | +| :------: | :----------------: | :----------------: | :--------------------: | :----------------: | :-------------: | :----------------: | :-------------: | :-------------: | :-------------: | +| 本地存储 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | +| 阿里云盘 | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| 115 | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| 百度云盘 | :x: | :x: | :question: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| 天翼云盘 | :x: | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| 迅雷云盘 | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | +| PikPak | :x: | :x: | :question: | :x: | :x: | :white_check_mark: | :x: | :x: | :no_entry_sign: | +| 谷歌云盘 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :no_entry_sign: | +| OneDrive | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :no_entry_sign: | 1. 这里提到的**阿里云盘**是指的[**阿里云盘Open**](../../guide/drivers/aliyundrive_open.md) 2. **OneDrive** 是因为已经没有了帐号暂时没有进行测试,如果有进行测试有效无问题,欢迎反馈 @@ -66,28 +35,12 @@ top: 130 4. 将**谷歌云盘**文件秒传到**天翼云盘** 和 **天翼云盘**将文件秒传到**天翼云盘**,可能有一些问题,并不是很稳定,需要留意观察 5. **百度云盘**只能从本地存储秒传到网盘,如果是使用跨存储进行复制秒传只能先将文件下载下来再进行秒传行为 -::: - -### How to use { lang="en" } - -### 使用方法 { lang="zh-CN" } - -::: en - -1. Right-click the file you want to transfer in the list, click Copy, and then select the folder to transfer to. -2. If there are multiple files, you can select the entire folder or open the checkbox in the lower right corner to select multiple files, and then there is a row of buttons at the bottom to click to copy. -
- Rapid_upload - Rapid_upload -
-::: - -::: zh-CN +### 使用方法 1. 在列表右键选择需要传输的文件点击复制,然后选择传输到那个文件夹就可以, 2. 如果是多个文件可以选择整个文件夹或者右下角打开复选框多选文件,然后最底部有一排按钮可以点击复制 +
秒传 秒传
-::: diff --git a/pages/guide/advanced/s3.md b/pages/guide/advanced/s3.md index bf6e8196f..1366f4a3f 100644 --- a/pages/guide/advanced/s3.md +++ b/pages/guide/advanced/s3.md @@ -1,56 +1,26 @@ --- -title: - en: S3 - zh-CN: 对象存储 categories: - guide - advanced top: 40 --- -::: en -This function is to map OpenList as the S3 protocol. You can use the [S3 client](#s3-client-recommendation-fill-in-sample), just as OpenList can be mapped to WebDAV. +# 对象存储 -- It can be the entire OpenList (root directory) or a separate folder - -::: -::: zh-CN 此功能是将 OpenList 映射为 S3 协议,可以使用 [S3客户端](#s3-客户端推荐-填写示例) 使用和查看,就像 OpenList 可以映射为 WebDAV 一样 - 可以是整个OpenList(根目录),也可以是单独某个文件夹 -::: - -## 1. How to use { lang="en" } - -## 1. 使用方式 { lang="zh-CN" } - -::: en -1.1 If it is for the first time, first open OpenList Manage to generate `S3 access key id` and `S3 secret access key` to save, then stop OpenList, open the configuration file to enable the `S3` service - -- If you update ID and secrets in the future, update and save, and then restart OpenList to take effect - -::: +## 1. 使用方式 -::: zh-CN 1.1 如果是第一次使用,先打开OpenList后台生成一下 `访问客户端ID` 和 `访问秘钥` 保存,然后停止 OpenList,打开配置文件启用 `S3` 服务 - 如果你后续更新访问ID和秘钥,更新保存,再重启OpenList才可以生效 -::: - ![](/img/advanced/s3/s3_config.png) -::: en -1.2 Open the [Configuration File](../../configuration/configuration.md#s3), change the `enable` field to` true` to enable S3 service - -::: - -::: zh-CN 1.2 打开 [配置文件](../../configuration/configuration.md#s3),将`enable`字段改成`true`启用S3服务 -::: - ```json{2} "s3": { "enable": true, @@ -59,39 +29,13 @@ This function is to map OpenList as the S3 protocol. You can use the [S3 client] } ``` -::: en -1.3 After the first two steps are completed, Start OpenList, open the Manage `s3` configuration page, and choose to store a certain path as an object to store the barrel - -- On the left is the name of the storage bucket displayed outside, and the right is the existing list directory path of OpenList. If you want to map the entire root directory, you can choose the `root` path in the list - - There may be errors in manual filling. It is recommended to use the `Choose` button on the right side for selection - ![](/img/advanced/s3/s3_add_backup.png) - -::: - -::: zh-CN 1.3 前两步操作完成后启动 OpenList,继续进入后台`S3`配置页面,选择自己想将某路径映射为对象存储存储桶 - 左侧是对外显示的存储桶名字,右侧是 OpenList 已有的列表目录路径,如果想将整个根目录全部映射出去可以可以选择列表中的 `root` 路径 - 手动填写可能会出现错误,建议使用右侧 `选择` 按钮进行选择 ![](/img/advanced/s3/s3_add_backup.png) -::: - -## 2. Use instructions { lang="en" } - -## 2. 使用说明 { lang="zh-CN" } - -::: en - -1. For ordinary object downloads, the transfer mode depends on the storage and proxy settings: - - If proxying is not required and the storage provides a direct download URL, OpenList may return an HTTP 302 redirect to the storage provider. - - If **Web Proxy** is enabled for the target storage, or another driver or proxy rule requires proxying, OpenList serves the response body through the local server. -2. A direct redirect saves OpenList bandwidth, but the S3 client must follow a cross-host redirect and accept the provider URL's access requirements. Server relay uses OpenList bandwidth and is a safer starting point for clients that cannot use the direct provider redirect. For repository tools such as restic or rustic, enable **Web Proxy** unless direct mode has been verified. -3. Currently only supports the use of client operations `List`, `get`, `put`, `del`, other operations are not supported yet -4. Currently only supports the use of the `S3 Client` to operate, Mount S3 to the `OpenList Object Storage` temporarily unavailable - -::: -::: zh-CN +## 2. 使用说明 1. 对于普通对象下载,传输方式取决于存储和代理设置: - 如果不需要代理,并且存储提供了直链下载地址,OpenList 可能返回 HTTP 302,将客户端重定向到存储提供商。 @@ -100,11 +44,7 @@ This function is to map OpenList as the S3 protocol. You can use the [S3 client] 3. 目前仅支持使用客户端 `获取列表`、`下载`、`上传`、`删除` 操作,其它操作暂未支持 4. 目前仅支持使用`S3客户端`来进行操作,挂载到OpenList对象存储暂时无法使用 -::: - -## 3. S3 client recommendation/fill in sample { lang="en" } - -## 3. S3 客户端推荐/填写示例 { lang="zh-CN" } +## 3. S3 客户端推荐/填写示例 ### 3.1. S3 Browser diff --git a/pages/guide/advanced/search.md b/pages/guide/advanced/search.md index 167231e0b..589d208b4 100644 --- a/pages/guide/advanced/search.md +++ b/pages/guide/advanced/search.md @@ -1,28 +1,14 @@ --- -title: - en: Search - zh-CN: 搜索/索引 categories: - guide - advanced top: 100 --- -### how to use { lang="en" } +# 搜索/索引 -### 如何使用 { lang="zh-CN" } +### 如何使用 -::: en -::: danger Follow the steps below to enable search: - -1. Go to the `indexes` page to select a **Search index** and click `Save`. -2. After the index is saved, click `Build indexes` to build the index. -3. Now you can search for files by click the search block on the top right corner of the page or by using the shortcut `Ctrl + K`. - -:exclamation: If you do not follow the above prompts, the prompt will be opened: **Search Not available** -::: - -::: zh-CN ::: danger 按照以下步骤开启搜索: 1. 转到`索引`页,选择一个**搜索索引**,并单击`保存`; @@ -32,48 +18,7 @@ top: 100 :exclamation: 若不按照上述提示开启会提示:**Search not available** ::: -## Difference between different search indexes { lang="en" } - -## 不同搜索索引之间的差异 { lang="zh-CN" } - -::: en - -- `database`: Search by database, which is using the existing data.db. It will create a new table, record the parent directory, name, and size of every object, but the search does not split words which means that match whether the keywords you enter appear in the name of object. In general, if you don't have a specific search requirement, we recommend you choose it. - -- `database (non-full-text search)`: The full-text search mode is used above, but full-text search will have some strange problems when using **MySQL database** as an OpenList database, which has not been resolved yet, so if your OpenList database Change to **MySQL**, and your OpenList version **`≥3.9.1`** It is recommended that you use this to build an index, although it is slower than full-text search and the gap is not very big, but it will not search for strange files , it’s more secure. After the future version is repaired, we will inform you to use the new full-text search to build the index. If you are using **sqlite3**, you can use whichever you like. - -- `bleve`: An open source full-text search engine. It will split the words in the name of object and search for the keywords you enter. But its search results may be so strange that you can't get the results you want, and it will take up more resources. - -- **sqlite3** is easy to trigger `database is locked` lock library cannot write files - - Solution to `database is locked`: - - It's because the database is building the index. If you are still in the building process, please wait patiently. - - If the index has been completed, it is caused by turning on [Automatically update the index](#automatically-update-the-index). Please turn off [Automatically update the index](#automatically-update-the-index). If the problem still occurs, please close and restart OpenList. - - Or switch the database to MySQL - -- `meilisearch`: A feature-rich, multilingual, blazing-fast search engine written in Rust. More accurate compared to `bleve`. - Requires [self-hosting](https://www.meilisearch.com/docs/learn/self_hosted/getting_started_with_self_hosted_meilisearch) or using a cloud service. - `OpenList` uses "http://localhost:7700" as the default meilisearch host, - if you host `meilisearch` and `OpenList` together locally with `meilisearch` authentication disabled, `OpenList` will automatically connect it, - otherwise you need to modify the **meilisearch** field in the configuration file (host, index UID, API key). - When `meilisearch` instance is protected by `api key`, the minimal actions of `api key` required by `OpenList` are `["search","indexes.get","settings.*","documents.*","tasks.*"]`. - Storage Space Usage: ~800MiB per 100,000 files (including folders), which may be larger or smaller depending on filename length and folder depth. Please note that the storage space occupied by `meilisearch` will increase as files are continuously added/updated, and space will not be released even if you delete some or all documents from the index, unless you create a refresh instance. Generally, you don't need to worry too much about this situation, as the storage space usage will stabilize at a certain value with use, and will only have a significant impact on space usage when large numbers of files are added. - - Download:https://github.com/meilisearch/meilisearch/releases - - `meilisearch` Docs:https://www.meilisearch.com/docs/ - - Reference:https://github.com/AlistGo/alist/discussions/6830 - -The following table could help you understand the difference between these search indexes quickly: - -| | database(full text search) | Database (non-full-text search) | bleve | meilisearch | -| ----------------------- | ----------------------------------------------- | ------------------------------------------------------------------------ | ----------- | ----------------------------------------- | -| Search results | Can't search in Chinese | More accurate than full-text search, you can search Chinese | Fuzzy match | Support CJK tokenizers & Chinese variants | -| Search speed | Fast,see above for advantages and disadvantages | Slower than full-text search, see above for advantages and disadvantages | Fast | Blazing fast | -| Specify folder search | Yes | Yes | No | Yes | -| Disk usage | Low | Low | High | High | -| Auto incremental update | Yes | Yes | No | Yes | - -::: - -::: zh-CN +## 不同搜索索引之间的差异 - `数据库`:按数据库搜索,它使用现有的 data.db。它将创建一个新表,记录父目录、名称和每个对象的大小,但搜索不拆分单词,这意味着匹配您输入的关键字是否出现在对象的名称中。一般来说,如果您没有特定的搜索要求,我们建议您选择它。 @@ -107,19 +52,6 @@ The following table could help you understand the difference between these searc | 硬盘占用 | 低 | 低 | 高 | 高 | | 自动增量更新 | 支持 | 支持 | 不支持 | 支持 | -::: - -::: en -::: warning -If you are using _MySQL_ as the database, it is recommended to use **`non-full-text search`** (strongly recommended) - -**`Non-full-text search`** Although it is not as fast as full-text search, it is not much slower. If you insist on using full-text search, you may have to sacrifice the inability to search Chinese - -If you use sqlite as the database, there is no full-text search, you can choose any database~ - -Full-text search: It will not search in the text of all files, don't get it wrong. -::: -::: zh-CN ::: warning 若你使用的是 **MySQL** 作为数据库,建议使用 **`非全文搜索`** (强烈推荐) @@ -130,66 +62,9 @@ Full-text search: It will not search in the text of all files, don't get it wron 全文搜索:不是在所有文件里面进行文件的文字里面进行搜索,别理解错了。 ::: -### Deploy MeiliSearch for indexing using Docker Compose { lang="en" } - -### 使用docker compose部署meilisearch进行索引 { lang="zh-CN" } - -#### Deploy MeiliSearch using Docker Compose { lang="en" } - -#### 使用docker compose部署meilisearch { lang="zh-CN" } - -::: en - -Feeling confused? - -Here's a Compose example to add `meilisearch` to your Openlist Compose. Follow the steps to set up indexing with `meilisearch`. - -```yaml -version: '3.3' -services: - openlist: - image: 'openlistteam/openlist:beta-aio' - container_name: openlist - volumes: - - '/tmp/data/Docker/OpenList:/opt/openlist/data' - ports: - - '5244:5244' - environment: - - UMASK=022 - networks: - - openlist - restart: unless-stopped - - meilisearch: - image: getmeili/meilisearch:v1.16 - container_name: meilisearch - ports: - - '7700:7700' - volumes: - - /tmp/data/Docker/meilisearch/meili_data:/meili_data - command: meilisearch --schedule-snapshot --snapshot-dir /meili_data/snapshots - environment: - - MEILI_MASTER_KEY=your_master_key_here - tty: true - stdin_open: true - networks: - - openlist - restart: unless-stopped - -networks: - openlist: - driver: bridge -``` - -Configuration Explanation: - -- `UMASK`: Sets file permissions -- `getmeili/meilisearch:v1.16`: Official recommendation to use a fixed version number (latest version at time of writing is `v1.16`). You may check the latest version yourself via the [official local deployment docs](https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally). -- Additional parameters for meilisearch can be modified via the [official local deployment docs](https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally) or deployed using alternative methods. +### 使用docker compose部署meilisearch进行索引 -::: - -::: zh-CN +#### 使用docker compose部署meilisearch 感到一头雾水? @@ -238,18 +113,6 @@ networks: - `getmeili/meilisearch:v1.16`: 官方建议使用固定的版本编号(本文撰写时最新版本为`v1.16`),您可以自行从[官网本地部署docs](https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally)查询最新版本。 - 有关meilisearch的其他参数可以通过[官网本地部署docs](https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally)自行修改或采用其他方式部署。 -::: - -::: en -::: warning -The `/tmp/data/Docker` directory here is merely an example; please store your data in an appropriate location. - -Additionally, the `MEILI_MASTER_KEY` here should be replaced with a key you generate yourself. You should substitute it with any alphanumeric string of 16 bytes or more. In most cases, one character corresponds to one byte. - -PS: This provides only a basic example. Please modify it according to your needs and add other configurations as required. -::: - -::: zh-CN ::: warning 这里的`/tmp/data/Docker`仅是一个示例,请将你的数据存储在合适的位置。 @@ -258,32 +121,8 @@ PS: This provides only a basic example. Please modify it according to your needs PS:这里仅仅提供一个基础示例,请根据你的需求进行修改和添加其他配置。 ::: -#### Setting up meilisearch in openlist { lang="en" } - -#### 在openlist中设置使用meilisearch { lang="zh-CN" } - -::: en -How to configure MeiliSearch in OpenList +#### 在openlist中设置使用meilisearch -First, you need to modify config.json. In this example, it should be located at `/tmp/data/Docker/OpenList/config.json`. - -Edit the section below, filling in the IP address and port of your MeiliSearch instance, along with the key you set in `MEILI_MASTER_KEY`. - -```json - "meilisearch": { - "host": "http://meilisearch:7700", - "api_key": "your_master_key_here", - "index": "openlist" - }, -``` - -Next, open the management panel, click `Indexes`, select `meilisearch`, and click `Refresh` to rebuild the index. - -![](/img/advanced/index_settings_panel.png){width=600px} - -::: - -::: zh-CN 如何在openlist设置meilisearch 首先你需要修改config.json,在这个示例中,它应该位于`/tmp/data/Docker/OpenList/config.json`。 @@ -302,45 +141,15 @@ Next, open the management panel, click `Indexes`, select `meilisearch`, and clic ![](/img/advanced/index_settings_panel.png){width=600px} -::: - -## Search tips { lang="en" } - -## 搜索提示 { lang="zh-CN" } - -::: en - -- If you want to search for a specific folder, you must choose `database` as the search index; -- If you choose `database` as the search index and the type of your database is `sqlite3`, we suggest that you don't make any changes in the admin page while building the index, as sqlite3 does not support concurrent writes and can cause `database-lock` issues; -- If you choose `bleve` as the search index, and if you want to search for new files or if you don't want to search for deleted files, the index needs to be completely rebuilt to take effect because `bleve` does not support incremental updates; -- But for `database`, it supports incremental updates, so you can search for new files or deleted files just by access the modified folder (and click `refresh` icon if cached) without rebuilding the index, which is much more convenient than `bleve`. - -::: -::: zh-CN +## 搜索提示 - 如果你想搜索特定的文件夹内的文件,你可以选择`数据库`或`meilisearch`作为搜索索引; - 如果你选择`数据库`作为搜索索引,你的数据库类型是`sqlite3`,我们建议你在创建索引时不要在管理页面做任何更改,因为 `sqlite3` 不支持并发写,可能导致`数据库锁定`问题; - 如果你选择`bleve`作为搜索索引,如果你想搜索新文件或不想搜索已删除的文件,索引需要完全重建才能生效,因为`bleve`不支持增量更新; - 但对于`数据库`/`meilisearch`,它支持增量更新,所以你可以搜索新的文件或删除的文件,只需访问修改的文件夹(并单击'刷新'图标,如果缓存),无需重建索引,这比`bleve`方便得多。 -::: - -### Ignore paths { lang="en" } - -### 忽略路径 { lang="zh-CN" } - -::: en -Paths to be skipped during index building, one path per line, multiple lines can be filled +### 忽略路径 -- Example: - - `/aaa network disk` - - `/bbb network disk/ccc folder` - -If you don't want to configure this, you can turn on the `disable index` option in each driver - -::: - -::: zh-CN 构建索引期间跳过填写的路径,一行一个路径,可多行填写 - 例子: @@ -349,21 +158,7 @@ If you don't want to configure this, you can turn on the `disable index` option 如果不想(不会)配置这里,可以去每个驱动中将`禁用索引`选项打开 -::: - -## Update index { lang="en" } - -## 更新索引 { lang="zh-CN" } - -::: en - -- (formerly: the path to update the index) - After building all the indexes, or a file has a large number of file updates, but it is inconvenient to rebuild, you can use this to update the index -- Example: - /aaa network disk - /bbb network disk/ccc folder - -::: - -::: zh-CN +## 更新索引 - (原:要更新索引的路径) 构建完所有索引后,或者某文件有大批量文件更新,但是又不方便点重新构建就可以使用这个来更新一下索引 @@ -371,37 +166,8 @@ If you don't want to configure this, you can turn on the `disable index` option - `/aaa网盘` - `/bbb网盘/ccc文件夹` -::: - -### Automatically update the index { lang="en" } - -### 自动更新索引 { lang="zh-CN" } - -::: en -:warning: **`The default is off, and the index will not be built automatically`**. - -For example, you have already built the index, but added a **network disk mount** or **folder update** later. - -But you have already built a lot of indexes. According to the previous words, there are two methods. - -1. Go in folder by folder before building - -2. Or it is cumbersome to refactor all - - But this time, just turn on the **`Automatically build index`** button and enter the **Newly mounted network disk** or **Updated folder**, the indexed files in this directory and The folder automatically builds the index without entering a folder by folder to let him build it automatically +### 自动更新索引 -- Advantages: Don't worry, all the indexes in this folder can be automatically built if there is an update into the root directory of the updated folder -- Cons: always on call ready to build - ---- - -Someone will find out that [**Path to update index**](#Path to update index) can also be updated? Can be updated but the two do not conflict. - -- [**Automatically update index**](#automatically-update-the-index): suitable for users who build indexes for all files -- [**Update Index**](#update-index): Suitable for **not** to build indexes for all files, but there are files that need to be built, manually build indexes to avoid all being indexed - -::: -::: zh-CN :warning: **`默认是关闭状态,不自动构建索引`**。 例如你已经构建完毕索引,但是后面又添加一个 **网盘挂载** 或者 **文件夹更新**。 @@ -424,40 +190,13 @@ Someone will find out that [**Path to update index**](#Path to update index) can - [**自动更新索引**](#自动更新索引):适合将所有文件都构建索引的用户 - [**更新索引**](#更新索引):适合 **不** 将所有文件都构建索引,但是有文件需要构建,自己手动去构建索引避免所有的都被构建索引 -::: - -### Maximum index depth { lang="en" } - -### 最大索引深度 { lang="zh-CN" } +### 最大索引深度 -::: en -default 20. -The one shown outside is built manually, and the update index option selects the depth in the update index button. -Explanation: The directory can enter up to several layers. For example, if you have a folder with a depth of 30 layers, set it to 20, and only build the first 20 layers, and the remaining 10 layers will not be built. -::: -::: zh-CN 默认为20。 外面显示的是手动构建的,更新索引选项在更新索引按钮里面选择深度。 说明:目录最多进几层,例如你有一个文件夹深度多达30层文件夹,设置为20,只构建前20层,剩下的10层不进行构建。 -::: -## :warning: Precautions for use { lang="en" } - -## :warning: 使用注意事项 { lang="zh-CN" } - -::: en - -- OpenList **V2** and **v3** types of mounts cannot be built by default -- If you are using **MySQL** as the database, it is recommended that you use **database (non-full-text search)**, [**Click to view details to see the second item**](#difference-between-different-search-indexes) -- In the future version (**≥3.9.0 version**), V3 users can choose whether to allow others to mount your network disk and then index it :no_entry:**`Use with caution`**:no_entry: - - View details: [allow-indexing](/configuration/site#allow-indexing) - - Don't ask why V2 is not supported, because the V2 version is no longer maintained, so there is no follow-up -- Why not directly open V2 V3 index construction: **https://github.com/alist-org/alist/discussions/2529** -- After building an index, users without permissions can search for hidden file/folder solutions [click to view](meta.md#tips) - -::: - -::: zh-CN +## :warning: 使用注意事项 - OpenList **V2** 和 **v3** 类型的挂载默认不能构建 - 如果你使用的是 **MySQL** 作为数据库,推荐你使用 **数据库(非全文搜索)**, [**点击查看详情看第二条**](#不同搜索索引之间的差异) @@ -467,44 +206,8 @@ Explanation: The directory can enter up to several layers. For example, if you h - 为什么不直接开放V2 V3索引构建: **https://github.com/alist-org/alist/discussions/2529** - 构建索引后,没有权限的用户可以搜索到隐藏的文件/文件夹解决方案[点击查看](meta.md#tips) -::: - -## The database file is very large, what should I do if it is still the same after clearing the index? { lang="en" } - -## 数据库文件很大,清空索引后还是一样大怎么办? { lang="zh-CN" } - -::: en -Normal users do not modify the database options. They use the `sqlite` database to build indexes, which will cause the database file to be particularly large +## 数据库文件很大,清空索引后还是一样大怎么办? -- Data files, `Data` folders in the same directory in OpenList program,`data.db,data.db-shm,data.db-wal` - -After turning on the constructive index, the more the number you build, the larger the files. Finally, you accidentally occupy the machine's hard disk, and then click the clear index button. What should I do if the file is still as big? - -- This is caused by the cache of `sqlite`, there are two solutions: - 1. We use commands or tools to connect to `sqlite` database, input:**`VACUUM;`** - - ```sql - VACUUM; - ``` - - 2. After using the command to clean up, we replace it with `mysql` database before constructing indexes - - Sqlite replaced with mysql database tutorial:**[BV1iV4y1T7kh](https://www.bilibili.com/video/BV1iV4y1T7kh)** - - Comparison after cleaning the command: The picture above shows before cleaning up, and the following figure shows that after cleaning, you can execute several commands several times if there is no effect. - - ![](/img/advanced/sqlite-mysql.png) - ---- - -`data.db, data.db-shm, data.db-wal` when backup, when backup,`data.db-shm,data.db-wal` Do these two files need backup? - -- In the backup, stop the program first, and then backup. You only need to backup the `data.db` database file. The other two do not need to backup - -- It may be after you stop the program`data.db-shm,data.db-wal`will automatically disappear, don't worry - -::: - -::: zh-CN 正常用户都是没有修改数据库选项使用的是 `sqlite` 数据库来构建索引的,就会导致数据库文件特别大 - 数据库文件在OpenList同级目录下的`data`文件夹,`data.db,data.db-shm,data.db-wal` @@ -531,5 +234,3 @@ After turning on the constructive index, the more the number you build, the larg - 建议在备份时,先将程序停止,再进行备份,到时候可以只单独备份`data.db`数据库文件,另外两个可以不进行备份 - 有可能在你停止程序后`data.db-shm,data.db-wal`这两个文件会自动消失,也不用担心 - -::: diff --git a/pages/guide/advanced/sso.md b/pages/guide/advanced/sso.md index b9abd65e3..eda2c4021 100644 --- a/pages/guide/advanced/sso.md +++ b/pages/guide/advanced/sso.md @@ -1,40 +1,18 @@ --- -title: - en: Third-party single sign-on - zh-CN: 单点登录 categories: - guide - advanced top: 60 --- -## 1. Single sign-on client id { lang="en" } +# 单点登录 -## 1. 单点登录客户端ID { lang="zh-CN" } +## 1. 单点登录客户端ID -::: en -client ID. -::: -::: zh-CN 客户端ID。 -::: - -## 2. Single sign-on client secrets { lang="en" } - -## 2. 单点登录客户端秘钥 { lang="zh-CN" } -::: en -client secrets. -::: warning matters needing attention - -1. Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used -2. Only one of the four types of single sign-on can be enabled temporarily. If you used other single sign-on methods before and then changed to a new single Unbind (just one click) and then bind the single sign-on method you want to use now. -3. If you use **`GitHub,Microsoft,Google,DingTalk`**, you only need to fill in the client ID and secret key, and select the corresponding platform in the options. - - If you use **`Casdoor`** the following parameters need to be filled in, just follow the tutorial to fill in - -::: +## 2. 单点登录客户端秘钥 -::: zh-CN 客户端秘钥 ::: warning 注意事项 @@ -48,9 +26,7 @@ client secrets. ::: -## 3. Registration binding single sign-on { lang="en" } - -## 3. 注册绑定单点登录 { lang="zh-CN" } +## 3. 注册绑定单点登录 ## @@ -66,20 +42,7 @@ client secrets. ## -## 4. SSO automatically registers as an OpenList account { lang="en" } - -## 4. 单点登录自动注册为OpenList帐号 { lang="zh-CN" } - -::: en - -- **OpenList Version > **v3.22.1** New Features** - Before using single sign-on to register as an OpenList account, we need to bind the single sign-on of OpenList first, the binding method is explained above -- Supports the `five` single sign-on methods mentioned above -- Do not use the default organization (**app-built-in**) directly, because all users in this organization are global administrator accounts -- Except `CASDOOR`, you only need to fill in the` Client id` and the `Client secrets`and the newly added single login account registered as OpenList account configuration - -::: -::: zh-CN +## 4. 单点登录自动注册为OpenList帐号 - **OpenList 版本 > **v3.22.1** 新增功能** 在使用单点登录注册为OpenList帐号前,我们需要先将OpenList的单点登录进行绑定,绑定方法在上面有说明 @@ -87,77 +50,25 @@ client secrets. - 如果使用`Casdoor`,请勿直接使用默认组织(**app-built-in**),因为这个组织内的用户都是全局管理员帐号 - 除了`Casdoor`其它的只需要填写`客户端ID`和`客户端秘钥`以及下面新增的单点登录帐号注册为OpenList帐号的配置 -::: +### 4.1. SSO完整填写示例 -### 4.1. SSO Full Fill Demo { lang="en" } - -### 4.1. SSO完整填写示例 { lang="zh-CN" } - -::: en -Please refer to the detailed description below for how to fill in, the schematic diagram is just a reference for filling in and not suitable for everyone’s user habits - -![](/img/advanced/sso-add.png) -::: - -::: zh-CN 分别如何填写看下面的详细说明,示意图只是一个填写参考并不适合每个人的用户习惯 ![](/img/advanced/sso-add.png) -::: -### 4.2. SSO auto register { lang="en" } +### 4.2. SSO自动注册 -### 4.2. SSO自动注册 { lang="zh-CN" } - -::: en -If we want SSO single sign-on to be registered as an OpenList account, we need to enable this option before it can be used -::: -::: zh-CN 如果我们想让SSO单点登录注册为OpenList帐号我们需要打开这个选项才可以使用 -::: - -### 4.3. SSO default dir { lang="en" } - -### 4.3. SSO默认路径 { lang="zh-CN" } - -::: en -That is to say, the default path used by the registered account is equivalent to the `Base path` in the OpenList user settings. -It can be the root directory `/`, or the path `/path/test/Demo` specified by the user +### 4.3. SSO默认路径 -![](/img/advanced/sso-dir.png) -::: -::: zh-CN 也就是说注册的帐号默认使用的路径,相当于OpenList用户设置里面的`基本路径` 可以是根目录`/`,也可以是用户指定的路径`/path/test/Demo` ![](/img/advanced/sso-dir.png) -::: - -### 4.4. SSO default permission { lang="en" } - -### 4.4. SSO默认权限 { lang="zh-CN" } - -::: en -It is equivalent to which permissions are enabled by default for registered users, as shown below - -![](/img/advanced/sso-permission_b.png#light) - -![](/img/advanced/sso-permission_h.png#dark) - -The default is 0, no permission is enabled - -If we need to enable some permissions during registration, we only need the sum of the numbers of different permissions. - -For example: -1. We need to open the user's `WebDav reading` and `WebDav manage ` by default, that is 256+512=768, we just fill in `768` in the options -2. If we need to open the three permissions of `Make dir or upload`, `Rename` and `Delete` by default when registering, then it is 8+16+128=152, we can fill in `152` in the background - I won’t say much about the examples, just add the permissions you need - -::: -::: zh-CN +### 4.4. SSO默认权限 相当于注册的用户默认开通哪些权限,就如下面所示的 ![](/img/advanced/sso-permission_b.png#light) @@ -170,55 +81,8 @@ For example: 2. 如果我们需要注册时默认开通`创建目录或上传`和`重命名`和`删除`这三个权限 那就是8+16+128=152,我们在后台填写`152`即可 例子就不多说了,需要那个权限自己相加就可以 -::: - -### 4.5. Precautions and instructions { lang="en" } - -### 4.5. 注意事项以及说明 { lang="zh-CN" } - -::: en -**4.1-There is already this user in the OpenList user database** - -| username | password | base\*path | role | permission | opt_secret | github_id | disabled | sso_id | -| ------------------ | -------- | ---------- | ---- | ---------- | ---------- | --------- | -------- | ----------- | -| anyi | FzdDfkmU | /test1 | 0 | 3 | | | 0 | | -| anyi**\*dc188911** | RUCtgqCw | /test1 | 0 | 3 | | | 0 | **dc18891** | - -As shown in the above table, a string of redundant ids is added after the newly registered single sign-on user name - -This is because the same user already exists in the OpenList user database, so the sso_id is also added after the user name - -If your newly registered single sign-on user name does not exist in the OpenList user database, it will not add the sso_id after the name - -**4.2-What should I do if I don’t want the SSO account to be registered as an OpenList account?** - -Just turn `SSO auto register` off,This will not affect the use of accounts that have been registered using Sso - -**4.3-If I turn off the single sign-on option, what should I do with the account registered with Sso?** +### 4.5. 注意事项以及说明 -Don't worry, after using single sign-on to register and log in to OpenList, log in in the background, and find `personal information` after logging in - -- You can modify **`username`** and **`password`** by yourself, save it after modification, so that you can log in with the OpenList account normally -- At this time, you can click `Unbind Single Sign-On Platform`, you can unbind or not unbind, and the subsequent [**default path**](#sso-default-dir) and [**default permissions**](#sso-default-permission) of this user can only be modified by the administrator in the OpenList background user - -**4.4-Why is this error code displayed when using sso?** - -```json{3} -{ - "code": 400, - "message": "The single sign on platform is not bound to any users: record not found", - "data": null -} -``` - -This is because [Single Sign-On Automatic Registration](#sso-auto-register) is not enabled for the OpenList account, and the single sign-on cannot be registered as an OpenList account - -- If you are an administrator, you can turn it on - -- If you are a user, you can contact the administrator to enable - -::: -::: zh-CN **4.1-OpenList用户数据库已存在同名的帐号** | username | password | base_path | role | permission | opt_secret | github_id | disabled | sso_id | @@ -258,23 +122,8 @@ This is because [Single Sign-On Automatic Registration](#sso-auto-register) is n - 如果你是管理员,你可以开启一下 - 如果你是用户,你可以去联系管理员开启 -::: - -## 5. Sso compatibility mode { lang="en" } - -## 5. SSO兼容模式 { lang="zh-CN" } - -::: en -OpenList single -point login is bound to Dingtalk, and then opens OpenList in the **`Dingtalk`** application, When you log in, choose Dingtalk login and jump to your computer browser. After authorization, you find that there is no response, because the browser cannot jump to the Dingtalk application inner browser page - -At this time, you need to open the **`sso compatability mode`** again to click on the login to log in successfully - -**OpenList applied in Dingtalk and opened as shown in the figure:** +## 5. SSO兼容模式 -![](/img/advanced/sso_cm.png) - -::: -::: zh-CN OpenList单点登录绑定了钉钉,然后在 **`钉钉`** 应用内打开OpenList,登录时选择钉钉登录会跳到自己电脑浏览器,授权后你会发现没有后续反应了,因为浏览器无法跳回钉钉应用內浏览器页面 这时候就需要打开 **`SSO兼容模式`** 再去点击登录就可以登录成功了 @@ -283,66 +132,22 @@ OpenList单点登录绑定了钉钉,然后在 **`钉钉`** 应用内打开Open ![](/img/advanced/sso_cm.png) -::: - -## 6. How to create the application in Dingtalk workbench { lang="en" } - -## 6. 如何创建钉钉工作台內应用 { lang="zh-CN" } +## 6. 如何创建钉钉工作台內应用 -::: en -A picture teaches you to create a new application and add it - -- LOGO is not modified when the newly -built newly built, you can wait for the newly built and then modify See_Figure_2 -- If you just browse OpenList in the Dingtalk application, you do n’t need a single -point login management, you do n’t need to set up a single -point login configuration, you can just add an application - ![](/img/advanced/sso_add_app.png) - -::: -::: zh-CN 一张图教你新建一个应用并且添加 - LOGO是默认的新建时不可修改,可以等新建好后再修改 (看图二) - 如果你只是在钉钉应用內浏览OpenList,不使用单点登录后台管理,可以不用设置单点登录配置只添加应用就可以 ![](/img/advanced/sso_add_app.png) -::: - -### 6.1. Modify the application logo { lang="en" } - -### 6.1. 修改应用LOGO { lang="zh-CN" } +### 6.1. 修改应用LOGO -::: en -Open [**DingTalk open platform management**](https://open-dev.dingtalk.com/fe/app#/corp/app) Find your newly built app to modify the logo. - -![](/img/advanced/sso_fix_logo.png) - -::: -::: zh-CN 打开 [**钉钉开放平台后台**](https://open-dev.dingtalk.com/fe/app#/corp/app) 找到你新建的应用进去修改LOGO就可以 ![](/img/advanced/sso_fix_logo.png) -::: - -## 7. redirect url { lang="en" } +## 7. 回调参数(重定向链接) -## 7. 回调参数(重定向链接) { lang="zh-CN" } - -::: en -After turning on **`Sso compatibility mode`** Then you need to log in to the corresponding software to modify the redirect url to change it to the two redirect url below - -- `GitHub` only needs to add one:`http(s)://You_Url/api/auth/sso_get_token` -- `Microsoft` and` Google` both need to be added, you can add multiple Redirect urls, so you only need to add two Redirect URLs. - -``` -http(s)://You_Url/api/auth/get_sso_id -http(s)://You_Url/api/auth/sso_get_token -``` - -If you enable the **Sso compatibility mode** if you do not modify it, you will prompt `Invalid Redirect URL` Error - -::: - -::: zh-CN 开启 **`SSO兼容模式`** 后需要将单点登录对应的软件内修改一下回调参数链接改成下面的两个回调参数 - `GitHub` 只需要添加一条:`http(s)://You_Url/api/auth/sso_get_token` @@ -354,5 +159,3 @@ http(s)://You_Url/api/auth/sso_get_token ``` 如果启用 SSO兼容模式 不修改则会提示 `invalid redirect url` 错误 - -::: diff --git a/pages/guide/advanced/sso/01-GitHub.md b/pages/guide/advanced/sso/01-GitHub.md index bb5b8e6f9..aa22614d1 100644 --- a/pages/guide/advanced/sso/01-GitHub.md +++ b/pages/guide/advanced/sso/01-GitHub.md @@ -1,45 +1,12 @@ -### 3.1. Github { lang="en" } +### 3.1. GitHub -::: en -::: warning -If you want to use GitHub to log in, you first need the machine you built OpenList to be able to connect to GitHub before you can call and use it, otherwise you cannot use it if the link is not connected -::: -::: en -Open **https://github.com/settings/developers** Click **`New OAuth App`** -::: - -### 3.1. GitHub { lang="zh-CN" } - -::: zh-CN ::: warning 若想使用GitHub登录,首选需要你搭建OpenList的机器能连接访问GitHub才可以调用使用,不然连接不上无法使用 ::: -::: zh-CN -打开 **https://github.com/settings/developers** 点击 **`New OAuth App`** -::: - -#### 3.1.1. Register OAuth Instructions { lang="en" } - -#### 3.1.1. Register OAuth 填写说明 { lang="zh-CN" } - -::: en - -- Application name - - Write whatever you want to call it -- **Homepage URL** - - home URL address - - Both **http** and **https** can be used -- Application description - - write whatever you want -- **Authorization callback URL** - - Callback URL address - - **https://your_domain/api/auth/sso_callback** - Both **http** and **https** can be used - Remember to get **Client secrets** after filling it out, and then fill it in the OpenList background. - Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used -::: +打开 **https://github.com/settings/developers** 点击 **`New OAuth App`** -::: zh-CN +#### 3.1.1. Register OAuth 填写说明 - Application name - 随便写想叫什么叫什么 @@ -53,21 +20,10 @@ Open **https://github.com/settings/developers** Click **`New OAuth App`** - **https://你的域名/api/auth/sso_callback** - 可以使用http https都可以 填写完毕后记得获取一下 **Client secrets**,然后填写到OpenList后台。 -::: - -#### 3.1.2. Completely fill in the reference schematic { lang="en" } - -#### 3.1.2. 完整填写参考示意图 { lang="zh-CN" } +#### 3.1.2. 完整填写参考示意图 -::: en -![sso](/img/advanced/github.png) -::: -::: zh-CN ![sso](/img/advanced/github.png) -::: - -#### 3.1.3. GitHub login Video Tutorials { lang="en" } -#### 3.1.3. GitHub视频教程 { lang="zh-CN" } +#### 3.1.3. GitHub视频教程 diff --git a/pages/guide/advanced/sso/02-Dingtalk.md b/pages/guide/advanced/sso/02-Dingtalk.md index 4f510f010..428ecee39 100644 --- a/pages/guide/advanced/sso/02-Dingtalk.md +++ b/pages/guide/advanced/sso/02-Dingtalk.md @@ -1,30 +1,4 @@ -### 3.2.Dingtalk { lang="en" } - -### 3.2.钉钉 { lang="zh-CN" } - -::: en -First open **https://open-dev.dingtalk.com/fe/app#/corp/app** - -In the upper right corner, first select `New Application`, select `H5 Micro Application` as the type, fill in the content by yourself and click Confirm to create - -Click on the new application and we will see the application credentials option, where `AppKey` is the client ID, and `AppSecret` is the client secret key - -- Just fill in the corresponding parameters in the OpenList background single sign-on - Go to the left column and find `Login and Share` \*\*Fill in the callback parameters `http://127.0.0.1:5234/api/auth/sso_callback` - -```bash title="Callback" parameter example -http://127.0.0.1:5244/api/auth/sso_callback -``` - -- Note: I used the callback parameter here for local testing. http://127.x When you use it, Write **http(s):\//your own domain name/api/auth/sso_callback** when filling in and using it by yourself - - Write the callback parameters well. Let’s go to the left column and find `Privilege Management`, find **`Personal Information Read Permission of Address Book`** and click to authorize - - Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used - -::: - -::: zh-CN +### 3.2.钉钉 首先打开 **https://open-dev.dingtalk.com/fe/app#/corp/app** @@ -46,15 +20,6 @@ http://127.0.0.1:5244/api/auth/sso_callback OpenList 后台参数也记得写好保存,写好保存后也要回到个人资料下方会有一个需要绑定的按钮进行绑定,否则无法使用 -::: - -#### 3.2.1. Completely fill in the reference schematic { lang="en" } +#### 3.2.1. 完整填写参考示意图 -#### 3.2.1. 完整填写参考示意图 { lang="zh-CN" } - -::: en -![sso](/img/advanced/dingding.png) -::: -::: zh-CN ![sso](/img/advanced/dingding.png) -::: diff --git a/pages/guide/advanced/sso/03-Microsoft.md b/pages/guide/advanced/sso/03-Microsoft.md index 982b42aa1..9615a1614 100644 --- a/pages/guide/advanced/sso/03-Microsoft.md +++ b/pages/guide/advanced/sso/03-Microsoft.md @@ -1,38 +1,5 @@ -### 3.3. Microsoft { lang="en" } +### 3.3. 微软 -### 3.3. 微软 { lang="zh-CN" } - -::: en -First open **https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade** - -Then register to create an application, I won't say much here, you can see the complete schematic diagram - -Account type must be selected: **Account in any organizational directory (any Azure AD directory - multi-tenant)** - -Microsoft callback parameters: must start with "HTTPS" or "http://localhost (I used localhost here for local testing) - -```bash title="Callback" parameter example -http://localhost:5244/api/auth/sso_callback?method=sso_get_token -http://localhost:5244/api/auth/sso_callback?method=get_sso_id -``` - -- Write **http(s):\//your own domain name/api/auth/sso_callback?method=sso_get_token** when filling in and using it by yourself - -- Write **http(s):\//your own domain name/api/auth/sso_callback?method=get_sso_id** when filling in and using it by yourself - -- Note: When adding a redirect URL to a new application, only one can be added. After the application is registered and registered, click on the application to see the options behind the redirect URI - - After filling it out, we click `Certificate and Password` on the left column to create a new `Client Password` to get our client secret key - -- After the client password is created, the **`value`** parameter is our `client key`, remember to save it, it will not appear again if it appears once, if you don’t save it in time, just create a new client password - - The client ID is in `Overview` at the top of the left column, find the application (client) ID, which is the client ID we need to fill in the OpenList - -- We have got the client ID and secret key and fill them in the OpenList single sign-on configuration. - Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used - -::: -::: zh-CN 首先打开 **https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade** 然后注册创建应用,我这里就不多说了可以看完整示意图 账户类型必须选择:**任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户** @@ -59,15 +26,6 @@ http://localhost:5244/api/auth/sso_callback?method=get_sso_id OpenList 后台参数也记得写好保存,写好保存后也要回到个人资料下方会有一个需要绑定的按钮进行绑定,否则无法使用 -::: - -#### 3.3.1. Completely fill in the reference schematic { lang="en" } +#### 3.3.1. 完整填写参考示意图 -#### 3.3.1. 完整填写参考示意图 { lang="zh-CN" } - -::: en -![sso](/img/advanced/weiruan.png) -::: -::: zh-CN ![sso](/img/advanced/weiruan.png) -::: diff --git a/pages/guide/advanced/sso/04-Google.md b/pages/guide/advanced/sso/04-Google.md index 08c0f9e97..0c1db5d35 100644 --- a/pages/guide/advanced/sso/04-Google.md +++ b/pages/guide/advanced/sso/04-Google.md @@ -1,41 +1,8 @@ -### 3.4. Google { lang="en" } +### 3.4. 谷歌 -### 3.4. 谷歌 { lang="zh-CN" } - -::: en -::: warning -If you want to use Google to log in, you first need the machine you built OpenList to be able to connect to Google before you can call and use it, otherwise you cannot use it if the link is not connected -::: - -::: en - -1. First open **https://console.cloud.google.com/projectselector2/apis/dashboard?hl=zh-cn** -2. If you are using it for the first time, you need to create a new project first (just write whatever you want, skip it if it has already been created) -3. Then configure the [consent screen](#agree-to-screen-configuration) (there is a separate instruction on how to configure the document, if it is already configured, skip it) -4. After configuring the unified screen, we click the credentials on the left, create credentials, and select OAuth client ID - -- Application Type Select Web Application, and write the name as you like -- Then add our two callback parameters in the authorized redirect URI - -```bash title="Callback" parameter example -http://127.0.0.1:5244/api/auth/sso_callback?method=get_sso_id -http://127.0.0.1:5244/api/auth/sso_callback?method=sso_get_token -``` - -- Write **http(s):\//your own domain name/api/auth/sso_callback?method=get_sso_id** when filling in and using it by yourself -- Write **http(s):\//your own domain name/api/auth/sso_callback?method=sso_get_token** when filling in and using it by yourself - After filling it out, click Create to get the OAuth client ID and secret key -- (It doesn't matter if you accidentally close it here, just click on the name of the application we created and enter it in the upper right position to see it) - We have got the client ID and secret key and fill them in the OpenList single sign-on configuration. - Remember to write and save the background parameters of OpenList. After writing and saving, you have to go back to the bottom of the personal data and there will be a button that needs to be bound, otherwise it cannot be used - -::: - -::: zh-CN ::: warning 若想使用Google登录,首选需要你搭建OpenList的机器能连接访问Google才可以调用使用,不然连接不上无法使用 ::: -::: zh-CN 1. 首先打开 **https://console.cloud.google.com/projectselector2/apis/dashboard?hl=zh-cn** 2. 如果是第一次使用需要先新建一个项目(随便写就行,如果已创建跳过) @@ -57,32 +24,13 @@ http://127.0.0.1:5244/api/auth/sso_callback?method=sso_get_token 客户端ID和秘钥我们都拿到了填写到OpenList单点登录配置里面去即可 OpenList 后台参数也记得写好保存,写好保存后也要回到个人资料下方会有一个需要绑定的按钮进行绑定,否则无法使用 -::: - -#### 3.4.1. Agree to screen configuration { lang="en" } - -#### 3.4.1. 同意屏幕配置 { lang="zh-CN" } +#### 3.4.1. 同意屏幕配置 -::: en -If it has been configured, just ignore this picture (you can zoom in if you can’t see it clearly) -![google-oauth-00](/img/drivers/google/google-oauth-eng-00.png) -![google-oauth-01](/img/drivers/google/google-oauth-eng-01.png) -![google-oauth-02](/img/drivers/google/google-oauth-eng-02.png) -::: -::: zh-CN 如已配置好 忽略本图即可(如果看不清楚可以放大) ![google-oauth-00](/img/drivers/google/google-oauth-00.png) ![google-oauth-01](/img/drivers/google/google-oauth-01.png) ![google-oauth-02](/img/drivers/google/google-oauth-02.png) -::: - -#### 3.4.2. Completely fill in the reference schematic { lang="en" } -#### 3.4.2. 完整填写参考示意图 { lang="zh-CN" } +#### 3.4.2. 完整填写参考示意图 -::: en ![sso](/img/advanced/google.png) -::: -::: zh-CN -![sso](/img/advanced/google.png) -::: diff --git a/pages/guide/advanced/sso/05-Casdoor.md b/pages/guide/advanced/sso/05-Casdoor.md index 779ce97d4..dd110b0b0 100644 --- a/pages/guide/advanced/sso/05-Casdoor.md +++ b/pages/guide/advanced/sso/05-Casdoor.md @@ -1,31 +1,5 @@ -### 3.5. Casdoor { lang="en" } +### 3.5. Casdoor -### 3.5. Casdoor { lang="zh-CN" } - -::: en -What is `Casdoor`? Yes, you can use other OAuth applications to log in. [**Self-deployment**](https://casdoor.org/docs/basic/server-installation) is open source and free, and official hosting requires payment. - -Now, Casdoor supports many OAuth application providers, as many as dozens of kinds, you have seen and not seen - -**GitHub open source link: https://github.com/casdoor/casdoor** - -After we enter `Casdoor`, we first create **Organization**1, **Token**2, **Application**3, **User**4 - -Do not use the default organization (**app-built-in**) directly, because all users in this organization are global administrator accounts - -Then fill in the `OpenList` backstage single sign-on option one by one. The user’s parameters are ignored for the time being. They are filled in when the personal data is bound to the single sign-on. - -![sso](/img/advanced/casdoor.png) - -After filling in the above parameters, we come to the personal data and click `Bind point single sign-on platform` to bind - -Then the `Casdoor` window will pop up, we can enter our registered user name - -![sso](/img/advanced/casdoor-user.png) - -::: - -::: zh-CN `Casdoor` 是什么? 是可以使用其他 OAuth 应用程序登录,[**自行部署**](https://casdoor.org/zh/docs/basic/server-installation)开源免费,使用他们官方托管是需要付费的。 现在,Casdoor 支持许多OAuth 应用程序提供者,多达几十种,你见过的没见过的都有 @@ -46,30 +20,12 @@ Then the `Casdoor` window will pop up, we can enter our registered user name ![sso](/img/advanced/casdoor-user.png) -::: - -#### 3.5.1. Access some other vendors on Casdoor { lang="en" } - -#### 3.5.1. 在Casdoor接入一些其他的厂商 { lang="zh-CN" } - -::: en -In addition to the four that `OpenList` has already connected to `GitHub Dingding Google Microsoft`, it is also connected to `QQ Baidu Feishu WeChat/Enterprise WeChat Douyin Bilibili` and so on, all [**click Check out all the manufacturers that can be accessed here**](https://casdoor.org/docs/provider/oauth/overview), of course, the four that `OpenList` has already accessed can also be added to `Casdoor` - -**View detailed tutorials on accessing other providers: [https://anwen-anyi.github.io/index/09-ssologin.html](https://anwen-anyi.github.io/index/09-ssologin.html)** -::: -::: en -::: details Direct iframe viewing - - - -::: +#### 3.5.1. 在Casdoor接入一些其他的厂商 -::: zh-CN 除了现在`OpenList`已经接入的 `GitHub 钉钉 谷歌 微软 `这四个除外还接入`QQ 百度 飞书 微信/企业微信 抖音 哔哩哔哩`等等等个,全部的[**点击这里查看全部可以接入的厂商**](https://casdoor.org/zh/docs/provider/oauth/overview),当然了`OpenList`已经接入的四个也是可以添加到`Casdoor` **查看详细接入其它提供商教程: [https://anwen-anyi.github.io/index/09-ssologin.html](https://anwen-anyi.github.io/index/09-ssologin.html)** -::: zh-CN ::: details 直接 iframe 查看 diff --git a/pages/guide/advanced/sso/06-Authentik.md b/pages/guide/advanced/sso/06-Authentik.md index a067e4d37..99f822bb7 100644 --- a/pages/guide/advanced/sso/06-Authentik.md +++ b/pages/guide/advanced/sso/06-Authentik.md @@ -1,46 +1,6 @@ ### 3.6. Authentik -#### 3.6.1. Authentik Setup { lang="en" } - -#### 3.6.1. Authentik 设置 { lang="zh-CN" } - -:::en -**Create a Provider for OpenList** - -1. Menu -> Applications -> Providers -> Create -2. Select `OAuth2/OpenID Provider` and click next -3. Enter an application name, this guide assumes you will call the provider `OpenList` -4. Select your authorization flow. The built-in `default-provider-authorization-implicit-consent` is acceptable -5. Make note of the `Client ID` and `Client Secret` fields as provided by Authentik - save these values for later -6. For Redirect UDIs/Origins, enter the following, replacing [your.openlist.domain] with the FQDN for your OpenList installation: - -```bash title="Callback" parameters -https://your.openlist.domain/api/auth/sso_callback\?method=sso_get_token -https://your.openlist.domain/api/auth/sso_callback\?method=get_sso_id -``` - - Please note the \ character before ? as an escape character for the regex used for this URI is mandatory. - -7. Make note of the signing key selected as you will need it later. This guide assumes you will use the default `authentik Self-signed Certificate` -8. Save the new provider - -**Create an Application for OpenList** - -1. Menu -> Applications -> Application -> Create -2. Enter an application name, recommended `OpenList` -3. An application slug of `openlist` will be automatically selected for you. This guide assumes you will keep this value -4. Select the provider by name you selected in Provider Setup step 3 - `OpenList` -5. Save the new application - -**Retrieve the JWT certificate** - -1. Menu -> System -> Certificates -2. Select the `>` next to the `authentik Self-signed Certificate`. If you chose another certificate for the application, select that certificate instead -3. Click Download Certificate to get a copy of the public JWT key - -::: - -::: zh-CN +#### 3.6.1. Authentik 设置 **为 OpenList 创建提供程序** @@ -77,29 +37,7 @@ https://your.openlist.domain/api/auth/sso_callback\?method=get_sso_id 2. 选择 `>` 旁边的 `authentik 自签名证书`。如果你为应用选择了其他证书,选择该证书 3. 点击“下载证书”以获取公共 JWT 密钥的副本 -::: - -#### 3.6.2. OpenList Setup { lang="en" } - -#### 3.6.2. OpenList 设置 { lang="zh-CN" } - -::: en - -- **Sso login enabled:** `yes` -- **Sso login platform:** `OIDC` -- **Sso client id:** [Client ID from Authentik] -- **Sso client secret:** [Client Secret from Authentik] -- **Sso oidc username key:** `preferred_username` -- **Sso organization name:** `user` -- **Sso application name:** `user` -- **Sso endpoint name:** `https://your.authentik.domain/application/o/openlist/` - - **Note:** Replace [your.authentik.domain] with the FQDN for your Authentik installation. Mind the trailing `/` at the end of the path. If you chose a different application slug in Authentik Application setup 3, substitute that here -- **Sso jwt public key:** Open the certificate file downloaded in step 3.3 of the Authentik Application setup and paste the contents here. It will start with `-----BEGIN CERTIFICATE-----` -- **Sso compatability mode:** `no` - -::: - -::: zh-CN +#### 3.6.2. OpenList 设置 - **启用 SSO 登录:** `是` - **SSO 登录平台:** `OIDC` @@ -113,5 +51,3 @@ https://your.openlist.domain/api/auth/sso_callback\?method=get_sso_id - **SSO JWT 公钥:** 打开在 Authentik 应用程序设置第 3.3 步中下载的证书文件,并将内容粘贴在此处。它以 `-----BEGIN CERTIFICATE-----` 开头 - **SSO 兼容模式:** `否` - -::: diff --git a/pages/guide/advanced/sso/07-keycloak.md b/pages/guide/advanced/sso/07-keycloak.md index 3ac32b091..9ba1df253 100644 --- a/pages/guide/advanced/sso/07-keycloak.md +++ b/pages/guide/advanced/sso/07-keycloak.md @@ -1,164 +1,77 @@ ### 3.7. Keycloak -::: zh-CN Keycloak 通过 OIDC(OpenID Connect)协议实现与 OpenList 的单点登录对接,可以作为 OpenList 的身份提供者,下面是配置步骤。 -::: -::: en -Keycloak is integrated via the OIDC (OpenID Connect) protocol and can serve as an identity provider for OpenList. The steps below explain how to configure Keycloak for OpenList SSO. -::: -#### 3.7.1. Keycloak 设置 { lang="zh-CN" } +#### 3.7.1. Keycloak 设置 -#### 3.7.1. Keycloak Setup { lang="en" } - -##### 3.7.1.1. Create Realm { lang="en" } - -##### 3.7.1.1. 新建 Realm { lang="zh-CN" } - -::: zh-CN +##### 3.7.1.1. 新建 Realm - 登录到 Keycloak 管理控制台。 - 在左侧导航栏点击 `Manage realms`,选择`Create Realm`新建领域,或选用现有领域。 -::: -::: en - -- Log in to the Keycloak admin console. -- Click `Manage realms` in the left navigation bar, select `Create Realm` to create a new realm or use an existing one. - -::: - -##### 3.7.1.2. Create Client { lang="en" } - -##### 3.7.1.2. 新建 Client { lang="zh-CN" } +##### 3.7.1.2. 新建 Client -::: zh-CN 选择领域后,在左侧菜单中找到 `Clients`并且进入该页面,点击`Create Client`。 -::: -::: en -After selecting the realm, click `Clients` → `Create Client`. -::: - **General settings** ![](/img/advanced/sso/keycloak-01.png) - ::: en - ::: details see the details - |Name|Value| - -|---- - |Client type|OpenID Connect| - |Client ID| OpenList (or any name you prefer)| - |Name| OpenList (or any name you prefer)| - |Description| optional| - |Always display in UI| select as needed| - - ::: - - ::: zh-CN ::: details 查看详情 - |配置名|配置值| - -|---- - |客户端类型|OpenID Connect| - |客户端ID| OpenList(或你喜欢的任何名称)| - |名称| OpenList(或你喜欢的任何名称)| - |描述| 可选| - |始终在 UI 中显示| 根据需要选择| + + | 配置名 | 配置值 | + | ---------------- | ------------------------------ | + | 客户端类型 | OpenID Connect | + | 客户端ID | OpenList(或你喜欢的任何名称) | + | 名称 | OpenList(或你喜欢的任何名称) | + | 描述 | 可选 | + | 始终在 UI 中显示 | 根据需要选择 | ::: - **Capability config** ![](/img/advanced/sso/keycloak-02.png) - ::: zh-CN + ::: details 查看详情 - |配置名|配置值| - -|---- - Client authentication| On| - Authorization| Off| - Authentication flow|`Standard Flow`,`Service account roles`| - PKCE Method | 留空| - Require DPoP bound tokens | Off| - ::: - ::: en - ::: details see the details - |Name|Value| - -|---- - Client authentication| On| - Authorization| Off| - Authentication flow|`Standard Flow`,`Service account roles`| - PKCE Method | Leave blank| - Require DPoP bound tokens | Off| + | 配置名 | 配置值 | + | ------------------------- | ---------------------------------------- | + | Client authentication | On | + | Authorization | Off | + | Authentication flow | `Standard Flow`,`Service account roles` | + | PKCE Method | 留空 | + | Require DPoP bound tokens | Off | ::: - **Login settings** ![](/img/advanced/sso/keycloak-03.png) - ::: zh-CN + ::: details 查看详情 - |配置名|配置值| - -|---- - Root URL |留空 - Home URL |填入你OpenList的访问地址,如:https://your.openlist.domain - Valid redirect URIs|`https://your.openlist.domain/api/auth/sso_callback?method=get_sso_id`
`https://your.openlist.domain/api/auth/sso_callback?method=sso_get_token`
`https://your.openlist.domain/api/auth/sso_get_token`
`https://your.openlist.domain/api/auth/get_sso_id` - Valid post logout redirect URIs|留空 - Web origins |留空 - ::: - ::: en - ::: details see the details - |Name|Value| - -|---- - Root URL |Leave blank - Home URL |Enter your OpenList access address, e.g.: https://your.openlist.domain - Valid redirect URIs|`https://your.openlist.domain/api/auth/sso_callback?method=get_sso_id`
`https://your.openlist.domain/api/auth/sso_callback?method=sso_get_token`
`https://your.openlist.domain/api/auth/sso_get_token`
`https://your.openlist.domain/api/auth/get_sso_id` - Valid post logout redirect URIs|Leave blank - Web origins |Leave blank + | 配置名 | 配置值 | + | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | Root URL | 留空 | + | Home URL | 填入你OpenList的访问地址,如:https://your.openlist.domain | + | Valid redirect URIs | `https://your.openlist.domain/api/auth/sso_callback?method=get_sso_id`
`https://your.openlist.domain/api/auth/sso_callback?method=sso_get_token`
`https://your.openlist.domain/api/auth/sso_get_token`
`https://your.openlist.domain/api/auth/get_sso_id` | + | Valid post logout redirect URIs | 留空 | + | Web origins | 留空 | ::: -##### 3.7.1.3. 获取JWT公钥 { lang="zh-CN" } - -##### 3.7.1.3. Retrieve JWT public key { lang="en" } - -::: zh-CN +##### 3.7.1.3. 获取JWT公钥 - 在左侧找到 `Clients`,选择刚创建的 Client 进入设置页面。 - 在 Client 设置页面,切换到 `Keys` 选项卡。 - 在 `Public Key` 字段中复制公钥内容,稍后在 OpenList 配置中使用。 - 如果没有看到公钥,可以点击 `Generate RSA Keys` 生成 -::: -::: en - -- In the left menu, find `Clients`, select the newly created Client to enter the settings page. -- In the Client settings page, switch to the `Keys` tab. -- Copy the content in the `Public Key` field for later use in OpenList configuration. -- If the public key is not visible, click `Generate RSA Keys` to generate it. - -::: - -##### 3.7.1.4. 获取Client Secret { lang="zh-CN" } - -##### 3.7.1.4. Retrieve Client Secret { lang="en" } - -::: zh-CN +##### 3.7.1.4. 获取Client Secret - 在 Client 设置页面,切换到 `Credentials` 选项卡。 - 在 `Client Secret` 字段中复制客户端密钥内容,稍后在 OpenList 配置中使用。 -::: -::: en - -- In the Client settings page, switch to the `Credentials` tab. -- Copy the content in the `Client Secret` field for later use in OpenList configuration. - -::: +#### 3.7.2. OpenList 设置 -#### 3.7.2. OpenList 设置 { lang="zh-CN" } - -#### 3.7.2. OpenList Setup { lang="en" } - -::: zh-CN 在 OpenList 的单点登录配置填写如下参数: - **启用 SSO 登录**:`是` @@ -176,24 +89,3 @@ After selecting the realm, click `Clients` → `Create Client`. 如有特殊需求,根据实际 Keycloak 部署调整 claims 和端点。 详细操作请参考 [Keycloak 官方 OIDC 文档](https://www.keycloak.org/documentation)。 - -::: - -::: en -Fill in the following parameters in OpenList's SSO configuration: - -- **SSO login enabled:** `yes` -- **SSO login platform:** `OIDC` -- **SSO client id:** Keycloak Client ID (from [Create Client](#_3-7-1-2-create-client) above) -- **SSO client secret:** Keycloak Client Secret (from [Retrieve Client Secret](#_3-7-1-4-retrieve-client-secret) above) -- **SSO oidc username key:** `preferred_username` or as per your Mapper settings -- **SSO organization name:** `master` (from [Create Realm](#_3-7-1-1-create-realm) above) -- **SSO application name:** `OpenList` (from [Create Client](#_3-7-1-2-create-client) above) -- **SSO endpoint name:** `https://your.keycloak.domain/realms/{realm-name}` (where realm-name matches the organization name) -- **SSO jwt public key:** from [Retrieve JWT public key](#_3-7-1-3-retrieve-jwt-public-key) above -- **SSO auto register as OpenList account:** Enable as needed -- **SSO compatibility mode:** `no` (enable if compatibility is required) - -Adjust claims and endpoints based on your specific Keycloak deployment if needed. -For detailed operations, refer to the [Keycloak official OIDC documentation](https://www.keycloak.org/documentation). -::: diff --git a/pages/guide/advanced/strm.md b/pages/guide/advanced/strm.md index 582af8c29..ac54765d9 100644 --- a/pages/guide/advanced/strm.md +++ b/pages/guide/advanced/strm.md @@ -1,11 +1,10 @@ --- -title: - en: Generate Strm - zh-CN: 生成 strm categories: - guide - advanced top: 10 --- +# 生成 strm + diff --git a/pages/guide/advanced/user.md b/pages/guide/advanced/user.md index f4093073b..9b3a99595 100644 --- a/pages/guide/advanced/user.md +++ b/pages/guide/advanced/user.md @@ -1,53 +1,24 @@ --- -title: - en: User - zh-CN: 用户 categories: - guide - advanced top: 140 --- -## Add user { lang="en" } +# 用户 -## 添加用户 { lang="zh-CN" } +## 添加用户 -::: en -::: warning Security Notice -Adding a user may expose files and server-side capabilities to another account. Use a strong, unique password, grant only the minimum required permissions, and create accounts only for trusted users. Security incidents resulting from improper user or permission management are the administrator's responsibility. -::: -::: zh-CN ::: warning 安全提醒 添加用户可能会使其他账户获得文件和服务器侧功能的访问能力。请使用强且唯一的密码,仅授予必要的最小权限,并仅为可信用户创建账户。因用户或权限管理不当而导致的安全事件,由管理员用户承担责任。 ::: -## Username { lang="en" } - -## 用户名 { lang="zh-CN" } +## 用户名 -::: en -Username for login. -::: -::: zh-CN 登录用户名。 -::: - -## Password { lang="en" } - -## 密码 { lang="zh-CN" } - -::: en -Password for login. -:::tip -Password is invalid for guest user. - -If you enter an incorrect password 6 times in a row, the current IP will be blocked for 30 minutes and you will not be able to enter your account and password to log in. However, it will not affect other IPs. It will only target IPs that entered 6 incorrect passwords. -- Restarting will immediately remove the 30-minute ban time +## 密码 -::: - -::: zh-CN 登录密码。 :::tip 密码对游客是无效的。 @@ -58,53 +29,15 @@ If you enter an incorrect password 6 times in a row, the current IP will be bloc ::: -## Base path { lang="en" } - -## 基本路径 { lang="zh-CN" } - -::: en -The root path that users see when he/she log in. +## 基本路径 -Q: **How to allow a user to see multiple folder paths?** - -A: You can create a new [alias](alias.md) storage, add all the paths you need to show the user to the alias, and then point to the newly created alias storage in the user path -::: -::: zh-CN 用户登录时看到的根路径。 Q:**如何否允许一个用户可以看到多个文件夹路径?** A:可以新建一个[别名](alias.md)存储,将你需要给用户展示的路径都添加到别名,然后在用户路径这里指向新建的别名存储。 -::: - -## Permission { lang="en" } - -## 权限 { lang="zh-CN" } - -::: en - -- Can see hides: Can see the hides files and folders -- Access without password: Can access without password -- Add offline download tasks: Add offline download tasks - - ⚠️ Granting a user remote file read/write permissions also grants them the ability to access resources from the server's network context, including internal network addresses. Only grant this permission to fully trusted users. Internal network access resulting from improper permission assignment is not considered a security vulnerability. -- Mkdir or upload: Can make directory or upload files -- Rename: Can rename files and folders -- Move: Can move files and folders -- Copy: Can copy files and folders -- Delete: Can delete files and folders -- Webdav read: Can read files and folders with webdav -- Webdav manage: Can manage files and folders with webdav -- FTP read: Can read files and folders with FTP -- FTP manage: Can manage files and folders with FTP -- Read archives: Read the contents of the file in the compressed package - - After turning on this option, compressed package format files will be previewed by default (as shown in the figure below), which will consume some server traffic, but will not download them all. - - If you want to turn off the preferred preview of the compressed format, **Manage => Setting => Preview by default when opening archives**, this option is turned off, and the preference is the download mode -- Decompress: Decompress compressed package files online - ![](/img/advanced/user_read_archives_light.png#light) - ![](/img/advanced/user_read_archives_dark.png#dark) -::: -::: zh-CN +## 权限 - 可以看到隐藏:可以看到隐藏的文件和文件夹 - 无密码访问:无需密码即可访问 @@ -126,37 +59,11 @@ A:可以新建一个[别名](alias.md)存储,将你需要给用户展示的路 ![](/img/advanced/user_read_archives_light.png#light) ![](/img/advanced/user_read_archives_dark.png#dark) -::: - -## Disabled { lang="en" } +## 停用 -## 停用 { lang="zh-CN" } - -::: en -After checking, this user will stop using it and cannot log in. The guest account is disabled by default. If you want to enable the guest account, please close it manually. -::: -::: zh-CN 勾选后将停止使用此用户,无法登陆,游客账户默认停用,如果要启用游客账户请手动关闭停用。 -::: - -## Tips { lang="en" } -## Tips { lang="zh-CN" } - -::: en - -1. Are you worried that visitors can see all files? [**Click to see how to set it up here**](../../faq/why.md#how-do-i-set-it-so-that-visitors-can-only-see-the-content-after-logging-in) -2. **`Guest user is disabled, login please`**: In order to protect your OpenList security, the guest access permission is closed, if you need guest access, open it yourself - - OpenList Manage --> users --> `guest` --> **Disable** uncheck -3. Question about **`Copy/Upload`** - - If you upload (including offline download and upload) large files, or upload a lot of files (hundreds or thousands), it is not recommended to use OpenList to operate, please go to the corresponding network disk official website to operate directly -4. Non-admin users can manage offline download, copy, upload and other operations in the background - - OpenList needs to be upgraded to version, and admin can also view the user's task progress and operations - ![](/img/advanced/user_manage.png) - -::: - -::: zh-CN +## Tips 1. 你是否在为游客能看到全部文件而发愁?[**点击查看这里如何设置**](../../faq/why.md#%E6%83%B3%E8%AE%A9%E6%B8%B8%E5%AE%A2%E7%99%BB%E5%BD%95%E5%90%8E%E6%89%8D%E8%83%BD%E7%9C%8B%E5%88%B0%E5%86%85%E5%AE%B9%E6%80%8E%E4%B9%88%E8%AE%BE%E7%BD%AE) 2. **`Guest user is disabled, login please`**:为了保护您的 OpenList 安全,游客访问权限关闭了,若需要游客访问自行打开 @@ -166,5 +73,3 @@ After checking, this user will stop using it and cannot log in. The guest accoun 4. 非 admin 权限用户在后台管理离线下载、复制、上传等操作 - 需要OpenList升级到版本,同时 admin 也可以查看用户的任务进度以及操作 ![](/img/advanced/user_manage.png) - -::: diff --git a/pages/guide/advanced/webdav.md b/pages/guide/advanced/webdav.md index 130cfe75e..73368cf80 100644 --- a/pages/guide/advanced/webdav.md +++ b/pages/guide/advanced/webdav.md @@ -1,42 +1,17 @@ --- -title: - en: WebDAV - zh-CN: WebDAV categories: - guide - advanced top: 50 --- -::: en -WebDAV (Web Distributed Authoring and Versioning) is a set of extensions to the Hypertext Transfer Protocol (HTTP) that enables users to collaboratively create, edit, and manage files directly on a web server. +# WebDAV -OpenList can be served as a WebDAV server, allowing users to access and modify files through a web interface. -::: -::: zh-CN WebDAV(Web 分布式创作和版本控制)是一种扩展超文本传输协议(HTTP)的协议,它允许用户使用 Web 服务器上的文件。 OpenList 可以作为 WebDAV 服务器,允许用户通过 Web 界面访问和修改网盘内的文件。 -::: - -## Permission Configuration Instructions { lang="en" } - -## 权限配置说明 { lang="zh-CN" } - -::: en - -To enable a specific user to use WebDAV, the following permissions must be enabled in the `User => Permissions` settings: - -1. **WebDAV Read** - - This permission must be enabled to **view and read** files and directories in WebDAV. - - If the user **only needs to view or play files**, enabling this permission is sufficient. -2. **WebDAV Management** - - This permission must be enabled to perform **write operations** (such as create, modify, delete, etc.). - - **Enabling only `WebDAV Management` is not enough!** You must also enable `WebDAV Management` **as well as** the specific file system permissions required for the planned operations (such as `rename`, `delete`, `copy`, `create directories or upload`, etc.). - -::: -::: zh-CN +## 权限配置说明 要使特定用户能够使用 WebDAV,需在 `用户 => 权限` 设置中为其开启以下权限: @@ -48,28 +23,7 @@ To enable a specific user to use WebDAV, the following permissions must be enabl - 必须开启此权限才能进行**写入操作**(创建、修改、删除等)。 - **仅开启 `WebDAV 管理` 还不够!** 需要同时开启 `WebDAV 管理` **以及** 其计划执行操作所需的具体文件系统权限(如 `重命名`、`删除`、`复制`、`创建目录或上传` 等)。 -::: - -## Basic Connection Configuration { lang="en" } - -## 基础连接配置 { lang="zh-CN" } - -::: en - -Use the following parameters to connect your WebDAV client: - -| Configuration Item | Value / Description | -| ------------------ | --------------------------------------------------------------------------------------- | -| **Url** | `http[s]://your-domain:port/dav/` | -| **Host** | Your domain (e.g., `openlist.example.com`) | -| **Path** | `dav` | -| **Protocol** | `http` or `https` (strongly recommend using **https** for security) | -| **Port** | The port **must be identical** to the one used for accessing the OpenList web interface | -| **Username** | The **username** you use to log into the OpenList web interface | -| **Password** | The **password** you use to log into the OpenList web interface | - -::: -::: zh-CN +## 基础连接配置 使用以下参数连接你的 WebDAV 客户端: @@ -83,53 +37,20 @@ Use the following parameters to connect your WebDAV client: | **用户名** | 你在 OpenList 网页端登录使用的**用户名** | | **密码** | 你在 OpenList 网页端登录使用的**密码** | -::: - -## Storage Support { lang="en" } - -## 存储支持 { lang="zh-CN" } +## 存储支持 -::: en -::: warning -Renaming during copy is not currently supported. -::: -::: zh-CN ::: warning 暂不支持复制时重命名。 ::: -## Client Software { lang="en" } - -## 客户端软件 { lang="zh-CN" } +## 客户端软件 -::: en -The following is a list of software that can be used to mount or access WebDAV services, categorized by platform: -::: -::: zh-CN 以下是一些可用于挂载或访问 WebDAV 服务的软件,按平台分类: -::: ### 🖥️ Windows -::: en - -- **File Managers / Mounting Tools:** - - [RaiDrive](https://www.raidrive.com/) (Recommended for mounting) - - [Mountain Duck](https://mountainduck.io/) (Mount as a disk) - - [rclone](https://rclone.org/) (Command line/mounting) - - [OneCommander](https://www.onecommander.com/) (File manager) - -- **Media Players (Direct Playback):** - - [PotPlayer](https://potplayer.daum.net/) - - [Kodi](https://kodi.tv/download) - - [AIMP](https://www.aimp.ru/) (Audio player) - -::: - -::: zh-CN - - **文件管理器 / 挂载工具:** - [RaiDrive](https://www.raidrive.com/) (推荐挂载) - [Mountain Duck](https://mountainduck.io/) (挂载为磁盘) @@ -140,28 +61,8 @@ The following is a list of software that can be used to mount or access WebDAV s - [Kodi](https://kodi.tv/download) - [AIMP](https://www.aimp.ru/) (音频播放器) -::: - ### 📱 Android -::: en - -- **File Managers:** - - [Solid Explorer](https://play.google.com/store/apps/details?id=pl.solidexplorer2) - - [MiXplorer](https://mixplorer.com/) (Manual APK installation required, open source) - - [X-plore File Manager](https://play.google.com/store/apps/details?id=com.lonelycatgames.Xplore) - - ES File Explorer - -- **Media Players (Direct Playback):** - - [nPlayer](https://play.google.com/store/apps/details?id=com.newin.nplayer.pro) - - [Kodi](https://kodi.tv/download) - - [Reex](https://play.google.com/store/apps/details?id=com.reex.reexplorer) - - [VLC for Android](https://www.videolan.org/vlc/download-android.html) (Open source) - -::: - -::: zh-CN - - **文件管理器:** - [Solid Explorer](https://play.google.com/store/apps/details?id=pl.solidexplorer2) - [MiXplorer](https://mixplorer.com/) (需手动安装 APK, 开源) @@ -173,23 +74,8 @@ The following is a list of software that can be used to mount or access WebDAV s - [Reex](https://play.google.com/store/apps/details?id=com.reex.reexplorer) - [VLC for Android](https://www.videolan.org/vlc/download-android.html) (开源) -::: - ### 🍎 iOS / iPadOS -::: en - -- **Media Players / File Managers (Direct Playback / Management):** - - [VidHub](https://zh.okaapps.com/product/1659622164) - - [nPlayer](https://apps.apple.com/us/app/nplayer/id1116905928) - - [Infuse](https://firecore.com/infuse) - - [Fileball](https://apps.apple.com/us/app/fileball-file-manager-player/id1615474435) - - [zFuse Player](https://apps.apple.com/us/app/zfuse-player/id1596223161) - -::: - -::: zh-CN - - **媒体播放器 / 文件管理器 (可直接播放/管理):** - [VidHub](https://zh.okaapps.com/product/1659622164) - [nPlayer](https://apps.apple.com/us/app/nplayer/id1116905928) @@ -197,45 +83,15 @@ The following is a list of software that can be used to mount or access WebDAV s - [Fileball](https://apps.apple.com/us/app/fileball-file-manager-player/id1615474435) - [zFuse Player](https://apps.apple.com/us/app/zfuse-player/id1596223161) -::: - ### 📺 TV (Android TV / Google TV) -::: en - -- **Media Players (Direct Playback):** - - [VidHub](https://zh.okaapps.com/product/1659622164) - - [nPlayer](https://play.google.com/store/apps/details?id=com.newin.nplayer.pro) - - [Kodi](https://kodi.tv/download) - -::: - -::: zh-CN - - **媒体播放器 (可直接播放):** - [VidHub](https://zh.okaapps.com/product/1659622164) - [nPlayer](https://play.google.com/store/apps/details?id=com.newin.nplayer.pro) - [Kodi](https://kodi.tv/download) -::: - ### 🍏 macOS -::: en - -- **File Managers / Mounting Tools:** - - [Mountain Duck](https://mountainduck.io/) (Mount as a disk) - - [rclone](https://rclone.org/) (Command line/mounting) - -- **Media Players (Direct Playback):** - - [VidHub](https://zh.okaapps.com/product/1659622164) - - [Infuse](https://firecore.com/infuse) - - [IINA](https://iina.io/) (Open source) - -::: - -::: zh-CN - - **文件管理器 / 挂载工具:** - [Mountain Duck](https://mountainduck.io/) (挂载为磁盘) - [rclone](https://rclone.org/) (命令行/挂载) @@ -244,56 +100,21 @@ The following is a list of software that can be used to mount or access WebDAV s - [Infuse](https://firecore.com/infuse) - [IINA](https://iina.io/) (开源) -::: - ### 🐧 Linux -::: en - -- **Mounting Tools / Command Line:** - - [rclone](https://rclone.org/) (Recommended, feature-rich) - - `davfs2` (System-level mounting, requires configuration) - -::: - -::: zh-CN - - **挂载工具 / 命令行:** - [rclone](https://rclone.org/) (推荐, 功能强大) - `davfs2` (系统级挂载, 需配置) -::: - -### 📝 Note-taking Software { lang="en" } - -### 📝 笔记软件 { lang="zh-CN" } - -::: en - -- [Joplin](https://joplinapp.org/) (Supports WebDAV sync for notes, open source) - -> **Feel free to contribute!** If you find other excellent and compatible WebDAV clients, feel free to recommend them. - -::: - -::: zh-CN +### 📝 笔记软件 - [Joplin](https://joplinapp.org/) (支持 WebDAV 同步笔记, 开源) > **欢迎补充!** 如果你发现其他优秀且兼容的 WebDAV 客户端,欢迎推荐。 -::: - -## Client Configuration Examples { lang="en" } - -## 客户端配置示例 { lang="zh-CN" } +## 客户端配置示例 -::: en -The interfaces of different software vary, but the key is to correctly fill in the information from the "Basic Connection Configuration" above. -::: -::: zh-CN 不同软件界面各异,核心是正确填写上面“基础连接配置”中的信息。 -::: ### nPlayer (iOS/Android) @@ -303,9 +124,7 @@ The interfaces of different software vary, but the key is to correctly fill in t ![](/img/guide/webdav/reex.png) -### ES File Explorer (iOS & Android) { lang="en" } - -### ES 文件浏览器 (iOS & Android) { lang="zh-CN" } +### ES 文件浏览器 (iOS & Android)
iOS @@ -324,8 +143,6 @@ The interfaces of different software vary, but the key is to correctly fill in t ![](/img/guide/webdav/potplayer.png) -### Synology NAS (Add via File Station) { lang="en"} - -### 群晖 NAS (通过 File Station 添加) { lang="zh-CN" } +### 群晖 NAS (通过 File Station 添加) ![](/img/guide/webdav/nas.png) diff --git a/pages/guide/drivers/115.md b/pages/guide/drivers/115.md index a62ab9c67..c7850673f 100644 --- a/pages/guide/drivers/115.md +++ b/pages/guide/drivers/115.md @@ -1,35 +1,22 @@ --- -title: - en: 115 Cloud / Share - zh-CN: 115 网盘 / 分享 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 697 -# A page can have multiple categories categories: - guide - drivers --- +# 115 网盘 / 分享 + -::: zh-CN ::: tip [**115 官方 Open API**](115_open.md) ::: -::: en -::: tip -[**115 Official Open API**](115_open.md) -::: -## 115 Cloud { lang="en" } +## 115 个人网盘 -## 115 个人网盘 { lang="zh-CN" } +### 视频教程(优先选择文字教程) -### 视频教程(优先选择文字教程) { lang="zh-CN" } - -::: zh-CN 视频教程有可能因为时间关系导致部分视频内功能不可用或产生变化 所以推荐优先选择文字教程,当然如果你看不懂文字教程或者嫌麻烦也可以观看视频教程 @@ -38,48 +25,16 @@ categories: -::: - -### Root folder ID { lang="en" } - -### 根文件夹 ID { lang="zh-CN" } - -::: en -Open the official website of 115 Cloud and click the string behind the url when you click into the folder you want to set, -such as , -which is `249163533602609229` -::: +### 根文件夹 ID -::: zh-CN 打开 115 网盘官网,点击进入要设置的文件夹时点击 URL 中 `cid`后面的数字 如 这个文件夹的 `根文件夹ID` 即为 `249163533602609229` -::: - -### Cookie获取方式 { lang="zh-CN" } - -### Cookie acquisition method { lang="en" } - -::: en -The following message is displayed when mounting a 115 cloud. This is because 115 has removed the three client applications of `Windows, Mac, and Linux`. - -```json -{ - "state": 0, - "error": "登录失败,系统已下架!如果你有电脑端的使用需求,我们诚挚邀请你下载体验115产品专属客户端“115浏览器”或在线使用“115网页端(115.com)”,畅享智能高效云生活。", - "errno": 0, - "message": "登录失败,系统已下架!如果你有电脑端的使用需求,我们诚挚邀请你下载体验115产品专属客户端“115浏览器”或在线使用“115网页端(115.com)”,畅享智能高效云生活。", - "code": 0 -} -``` -The cookies you have obtained from these three devices cannot be used. Please obtain the cookies from other devices again to mount them. +### Cookie获取方式 -::: - -::: zh-CN 挂载115云盘提示如下信息,是因为 115已经下架了 `Windows、Mac、Linux` 这三个客户端的应用 ```json @@ -94,64 +49,31 @@ The cookies you have obtained from these three devices cannot be used. Please ob 你获取的是这三个设备的Cookie自然就无法使用,请重新获取其它设备的Cookie进行挂载 -::: - -#### 1. QRCode scanning method login { lang="en" } +#### 1. QRCode 扫码方式登录 -#### 1. QRCode 扫码方式登录 { lang="zh-CN" } - -::: en -::: tip -Still under active development, please stay tuned! -::: - -::: zh-CN ::: tip 开发中, 教程暂未更新, 敬请期待! ::: -#### 2. 手动抓取 Cookie 方式登录 { lang="zh-CN" } - -#### 2. Log in by manually grabbing Cookies { lang="en" } - -::: en -The `cookie` can be obtained from the browser with your 115 account logined, or packet capture. -::: -::: en -::: tip -Please note that cookies should not end with `;`. -::: +#### 2. 手动抓取 Cookie 方式登录 -::: zh-CN Cookie 可以在浏览器登录账号时从浏览器获取,如 Chrome 浏览器可以使用 [115ToAlist插件](https://github.com/yenkn/115-to-alist) 自动同步 Cookie: ![115ToAlist tutorial](https://github.com/yenkn/115-to-alist/raw/main/docs/tutorial.png) 也可通过抓包 115 应用获取,有效期比较长。 -::: -::: zh-CN ::: tip 注意 Cookie 最后不要有`;`。 :::
-#### 3. 使用 Python 脚本获取 Cookie { lang="zh-CN" } - -#### 3. Get Cookies Using Python Script { lang="en" } - -::: en -Expand the detailed description to view the tutorial and script source code - -Source code from:[**https://gist.github.com/ChenyangGao/d26a592a0aeb13465511c885d5c7ad61**](https://gist.github.com/ChenyangGao/d26a592a0aeb13465511c885d5c7ad61) -::: +#### 3. 使用 Python 脚本获取 Cookie -::: zh-CN 展开详细说明进行查看教程和脚本源码 源码来自:[**https://gist.github.com/ChenyangGao/d26a592a0aeb13465511c885d5c7ad61**](https://gist.github.com/ChenyangGao/d26a592a0aeb13465511c885d5c7ad61) -::: ::: details View source @@ -318,58 +240,7 @@ if __name__ == "__main__": ::: -::: en - -1. You need to install [**Python 3.11.x**](https://www.python.org/downloads/) or above -2. If the QR code cannot be displayed properly in the `CMD` and `powershell` terminals, you can use the `-o` parameter to generate an image to scan the code, or you need to install an additional terminal - 1. Use the `-o` parameter to generate the image method and scan the QR code to confirm - - ````python - python main.py wechatmini -o - ``` - The devices that can be obtained are as follows. If you do not fill in the device, the default device on the `Web` side will be used - `Web`,`android`,`ios`,~~`linux`,`mac`,`windows`~~,`tv`,`alipaymini`,`wechatmini`,`qandroid` - ```` - - `alipaymini` and `wechatmini` They are Alipay Mini Program and WeChat Mini Program. - - **Windows、Mac、Linux should not be able to use it anymore. The official client was recently removed from the shelves.** - - It is recommended to use some devices that you do not commonly use, otherwise logging in will crowd out the previous ones. - *** - 2. Additional installation terminal - - Windows Store:**https://apps.microsoft.com/detail/9n0dx20hk701?rtc=1&hl=zh-cn&gl=CN** - - GitHub Download:**https://github.com/microsoft/terminal/releases** - 3. Other ways, solve it yourself - ---- - -Execute the command and obtain the QR code. Scan the QR code on the APP to obtain `Cookie` - -::: zh-CN - -1. 需要安装 [**Python 3.11.x**](https://www.python.org/downloads/) 以上版本 -2. 如果二维码在 `CMD` 和 `powershell` 两个终端无法显示正常,可以使用`-o`参数生成图片的方式来扫码,或者需要额外安装一个终端 - 1. 使用 `-o` 参数,生成图片方式然后扫码确认 - - ````python - python main.py wechatmini -o - ``` - 可以获取的设备有如下,如果不填写设备默认使用的是 `Web` 端的设备 - `Web`,`android`,`ios`,~~`linux`,`mac`,`windows`~~,`tv`,`alipaymini`,`wechatmini`,`qandroid` - ```` - - `alipaymini` 和 `wechatmini` 分别是支付宝小程序和微信小程序 - - **Windows、Mac、Linux 应该不能使用了,最近官方把客户端下架了** - - 建议使用一些自己不常用的设备,否则登录会将之前的挤掉 - *** - 2. 另外安装终端 - - Windows Store:**https://apps.microsoft.com/detail/9n0dx20hk701?rtc=1&hl=zh-cn&gl=CN** - - GitHub 下载:**https://github.com/microsoft/terminal/releases** - 3. 其它方式自行解决 - ---- - -执行命令,获取二维码 APP扫码获取 `Cookie` -::: - -##### 3.1. Direct Execution { lang="en" } - -##### 3.1.直接执行 { lang="zh-CN" } +##### 3.1.直接执行 ```shell PS C:\Users\233\Desktop\115> python --version @@ -391,16 +262,9 @@ PS C:\Users\233\Desktop\115> python main.py wechatmini ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ``` -::: en -Execute the command directly. The QR code will appear in the terminal for display. -::: -::: zh-CN 直接执行 二维码会出现在终端中进行显示 -::: - -##### 3.2. Using the -o parameter { lang="en" } -##### 3.2. 使用-o参数 { lang="zh-CN" } +##### 3.2. 使用-o参数 ```bash PS C:\Users\233\Desktop\115> python --version @@ -408,29 +272,9 @@ Python 3.12.2 PS C:\Users\233\Desktop\115> python main.py wechatmini -o ``` -::: en -After using the `-o` parameter, a QR code image will pop up automatically -::: - -::: zh-CN 使用 `-o` 参数后会自动弹出一张二维码图片 -::: - -### 秒传 { lang="zh-CN" } - -### Rapid upload { lang="en" } - -::: en - -- **v.3.27.0** version Enhanced Rapid upload: You can directly upload files with `Alibaba Cloud Disk Open` by copying - - The premise is to upload the file from 115 Rapid upload to Alibaba Cloud Disk Open, the file already exists in Alibaba Cloud Disk Open, otherwise it is a normal copy task. - - If you want to transfer 115 cloud disk files to Alibaba Cloud Disk in seconds, you need to turn on the Rapid upload option of Alibaba Cloud Disk, otherwise it will be uploaded in normal mode. - If you want to upload files using Miaochuan, it is recommended to build an OpenList locally on your home computer and add a `local storage` and `115 cloud disk` to copy Miaochuan to save resources. -
- -::: -::: zh-CN +### 秒传 - **v.3.27.0** 版本 增强秒传:可以直接通过复制方式来和`阿里云盘Open`进行相互秒传文件 - 前提是要从115秒传到阿里云盘Open的文件,阿里云盘Open已经存在,否则就是正常复制任务。 @@ -438,29 +282,8 @@ After using the `-o` parameter, a QR code image will pop up automatically 如果要使用秒传来上传文件建议在自己家用电脑本地搭建一个OpenList添加一个`本地存储`和`115云盘`进行复制秒传这样节省资源。
-::: - -### 离线下载 { lang="zh-CN" } - -### Offline Download { lang="en" } - -::: en -**v3.37.0** and above versions support calling `115 Cloud` offline download function in OpenList -Select in the lower right corner and select `115 Cloud` for offline download options - -- Support: `magnet`, `http`, `ed2k` links - Only 115 Cloud is supported for offline download. If it is not 115 Cloud, the following error message will be displayed, Although the offline download prompt was successfully added, an error will be prompted in the background. -- unsupported storage driver for offline download, only 115 Cloud is supported - ![](/img/drivers/offline_download_error.png) -- Some tips for using 115 offline downloads: - 1. Out of sync problems may occur (manual refresh in the lower right corner ) - 2. Currently, when the download is successful, completed tasks in the offline list are deleted. - 3. 115 Task URLs that are already in the offline list cannot be added again. -
+### 离线下载 -::: - -::: zh-CN **v3.37.0** 及以上版本支持在OpenList调用115离线下载功能 右下角选择 离线下载选项选择`115 Cloud` @@ -474,35 +297,7 @@ Select -::: - -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -523,86 +318,32 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: - -## 115 Share { lang="en" } - -## 115网盘分享 { lang="zh-CN" } +## 115网盘分享 -::: zh-CN ![url_demo](/img/drivers/115/115-url_demo.png) `分享链接ID`1 和 `分享链接提取码`2 分别如何获取一目了然 -::: -::: en -![url_demo](/img/drivers/115/115-url_demo.png) - -`Sharing link ID`1 and `Sharing link extraction code`2 How to obtain them respectively is clear at a glance. -::: - -### Root folder ID { lang="en" } - -### 根文件夹ID { lang="zh-CN" } - -::: en -The default is empty and the entire directory file is mounted. - -The folder IDs are the root folder ID and the subfolder ID respectively. The following demonstrates how to obtain the `shared root folder directory ID`1 and `other subfolder directory ID`2 respectively. -::: +### 根文件夹ID -#### 1. shared root folder directory ID { lang="en" } +#### 1. 分享根文件夹目录ID -#### 1. 分享根文件夹目录ID {lang="zh-CN"} - -::: en -Open developer mode (F12) and clear all requests first. Before we enter the folder, clear all requests first. - -Let's click on the root folder to enter. There will be a new request on the right, and then select `Load` to see the ID (cid) of our folder. - -![url_demo](/img/drivers/115/115-root_cid.png) -::: -::: zh-CN 打开开发者模式(F12)先将请求全部清空,我们进入文件夹,先将全部请求清空, 我们再点击根文件夹进入,右侧会有一个新的请求,然后选择`载荷`就能看到我们的这个文件夹的ID (cid) ![url_demo](/img/drivers/115/115-root_cid.png) -::: - -#### 2. other subfolder directory ID { lang="en" } -#### 2. 其它子文件夹目录ID { lang="zh-CN" } +#### 2. 其它子文件夹目录ID -::: zh-CN 打开开发者模式(F12)先将请求全部清空,我们进入文件夹,先将全部请求清空, 右侧会有一个新的请求,然后选择`预览` 然后进行展开查看,就可以看到其它子文件夹的ID (cid) ![url_demo](/img/drivers/115/115-file_cid.png) -::: -::: en -Open developer mode (F12) and clear all requests first. Before we enter the folder, clear all requests first. - -There will be a new request on the right, then select `Preview` and expand it to see the IDs (cid) of other subfolders. - -![url_demo](/img/drivers/115/115-file_cid.png) -::: - -### 错误提示 { lang="zh-CN" } -### Error Tips { lang="en" } +### 错误提示 -::: en -For example, the 115 sharing link shown in the picture below has expired, but the sharing link can still be opened. - -![url_demo](/img/drivers/115/115-add_error.png) - -But when adding and saving, the following error code will appear: - -`Failed init storage but storage is already created: failed init storage: failed to get share snap: json: cannot unmarshal number into Go struct field .data.shareinfo.share_state of type string ` -::: -::: zh-CN 例如下图所示的115分享链接分享过期,但是分享链接还能打开 ![url_demo](/img/drivers/115/115-add_error.png) @@ -610,36 +351,8 @@ But when adding and saving, the following error code will appear: 但是在添加保存时候会出现如下错误码: `Failed init storage but storage is already created: failed init storage: failed to get share snap: json: cannot unmarshal number into Go struct field .data.shareinfo.share_state of type string ` -::: - -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -659,5 +372,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/115_open.md b/pages/guide/drivers/115_open.md index b92320c94..bb8a531e7 100644 --- a/pages/guide/drivers/115_open.md +++ b/pages/guide/drivers/115_open.md @@ -1,122 +1,37 @@ --- -title: - en: 115 Open - zh-CN: 115 开放平台 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 698 -# A page can have multiple categories categories: - guide - drivers --- -::: en -::: tip -Developed with the official [**115 Open Platform API**](https://open.115.com) -::: -::: zh-CN +# 115 开放平台 + ::: tip 使用官方 [**115开放平台 API**](https://open.115.com) 开发 ::: -## 1. Necessary Conditions { lang="en" } - -## 1. 必要条件 { lang="zh-CN" } +## 1. 必要条件 -::: en -An **115** account is required. -::: -::: en -::: warning Note -The speed and stability are related to the local network environment, the network environment of the 115 server, and the performance of the machine running OpenList. -::: - -::: zh-CN 必须有 **115** 的帐号 -::: -::: zh-CN + ::: warning 注意事项 速度与稳定性与本地网络环境,115 服务器的网络环境以及 OpenList 的运行机器的性能有关 ::: -## 2. Preparation for Access { lang="en" } - -## 2. 准备接入 { lang="zh-CN" } +## 2. 准备接入 -### 2.1. Register an Application on the Open Platform (optional; no need to create if using the built-in key pair of OpenList/public welfare server/self-built server) { lang="en" } +### 2.1. 开放平台注册应用(可选,如果使用OpenList/公益服务器/自建服务器内置的密钥对,则不用创建) -### 2.1. 开放平台注册应用(可选,如果使用OpenList/公益服务器/自建服务器内置的密钥对,则不用创建) { lang="zh-CN" } - -::: en -::: tip -Please register the application according to the requirements of the 115 Open Platform. -::: -::: en -Open Platform address: [115 Open Platform: https://open.115.com](https://open.115.com) -::: -::: zh-CN ::: tip 请根据115 开放平台的要求注册应用 ::: -::: zh-CN -开放平台地址: [115 开放平台:https://open.115.com ](https://open.115.com) -::: - -### 2.2. Obtain Tokens { lang="en" } - -### 2.2. 获取令牌 { lang="zh-CN" } - -::: en - -1. Visit [api.oplist.org](https://api.oplist.org) **⚠️ If using a public welfare server/self-built server, visit the address of the public welfare server/self-built server**. - -2. Select **115 Network Disk Verification** from the drop-down box. - - ![115-open-01-d](/img/drivers/115/115-open-01-d.png#dark) - - ![115-open-01-l](/img/drivers/115/115-open-01-l.png#light) - -3. If you are using the built-in key pair of `OpenList (or public welfare server/self-built server)` (i.e., you do not have application information from the 115 Open Platform), please configure according to `3.1`, `3.2`, and `3.3`: - - 3.1. Check `Use parameters provided by OpenList`. - - 3.2. Leave both `Client ID (ClientID/AppID)` and `Application Secret (AppKey/Secret)` blank. - - 3.3. Click the `Get Token` button. - - ![115-open-02-01-d](/img/drivers/115/115-open-02-01-d.png#dark) - ![115-open-02-01-l](/img/drivers/115/115-open-02-01-l.png#light) - -4. If you are using your own created OAuth client ID and secret, please configure according to `4.1`, `4.2`, and `4.3`: - - 4.1. Do not check `Use parameters provided by OpenList`. - - 4.2. Enter your `AppId` in `Client ID` and your `AppSecret` in `Application Secret`. - - 4.3. Click the `Get Token` button. - - ![115-open-02-02-d](/img/drivers/115/115-open-02-02-d.png#dark) - - ![115-open-02-02-l](/img/drivers/115/115-open-02-02-l.png#light) - -5. In the pop-up window, log in to your 115 account and authorize OpenList to access your 115 network disk. - - ![115-open-03](/img/drivers/115/115-open-03.png) - -6. After successful authorization, the page will display your `Access Token` and `Refresh Token`. Please copy and save these two tokens. - - ![115-open-04](/img/drivers/115/115-open-04-d.png#dark) - - ![115-open-04](/img/drivers/115/115-open-04-l.png#light) - -::: +开放平台地址: [115 开放平台:https://open.115.com ](https://open.115.com) -::: zh-CN +### 2.2. 获取令牌 1. 访问[api.oplist.org](https://api.oplist.org) **⚠️如果使用公益服务器/自建服务器,请访问公益服务器/自建服务器的地址** @@ -160,30 +75,12 @@ Open Platform address: [115 Open Platform: https://open.115.com](https://open.11 ![115-open-04](/img/drivers/115/115-open-04-l.png#light) -::: - -## 3. Adding 115 Network Disk to OpenList { lang="en" } - -## 3. 在OpenList中添加115网盘 { lang="zh-CN" } +## 3. 在OpenList中添加115网盘 -### 3.1. Configuration Instructions { lang="en" } +### 3.1. 配置说明 -### 3.1. 配置说明 { lang="zh-CN" } +#### 3.1.1. 根文件夹 ID -#### 3.1.1. Root Folder ID { lang="en" } - -#### 3.1.1. 根文件夹 ID { lang="zh-CN" } - -::: en -The default root directory ID is: `0` - -Open the 115 network disk official website, and when clicking to enter the folder to be set, click the number after `cid` in the URL. - -For example, - -The `root folder ID` of this folder is `249163533602609229`. -::: -::: zh-CN 默认根目录ID为:`0` 打开 115 网盘官网,点击进入要设置的文件夹时点击 URL 中 `cid`后面的数字 @@ -191,35 +88,8 @@ The `root folder ID` of this folder is `249163533602609229`. 如 这个文件夹的 `根文件夹ID` 即为 `249163533602609229`
-::: -### 3.2. Start Adding { lang="en" } - -### 3.2. 开始添加 { lang="zh-CN" } - -::: en - -1. Open the management interface of OpenList and click `Storage` in the left menu. - -2. On the storage list page, click the `Add Storage` button in the upper right corner. - -3. Select the driver as `115 Open Platform`. - - ![115-open-05-d](/img/drivers/115/115-open-05-d.png#dark) - - ![115-open-05-l](/img/drivers/115/115-open-05-l.png#light) - -4. Enter the mounting path, such as: `115`. - -5. Enter the root folder ID obtained above in `Root Folder ID` (please refer to [3.1.1. Root Folder ID](#_3-1-1-root-folder-id)). - -6. Enter the obtained `Refresh Token` and `Access Token` in the refresh token (if not obtained, please refer to [2. Preparation for Access](#_2-preparation-for-access)). - - 115's token refresh mechanism does not require AppKey and has IP-based rate limiting, so it is implemented using [local logic](https://github.com/OpenListTeam/115-sdk-go). - -7. Click the `Add` button to complete adding the 115 network disk. - -::: -::: zh-CN +### 3.2. 开始添加 1. 打开 OpenList 的管理界面,点击左侧菜单中的`存储`。 @@ -240,24 +110,7 @@ The `root folder ID` of this folder is `249163533602609229`. 7. 点击`添加`按钮,完成115网盘的添加。 -::: - -### 3.3. Method for Refreshing Access Token { lang="en" } - -### 3.3. 当前AccessToken刷新的方式 { lang="zh-CN" } - -::: en - -```mermaid -sequenceDiagram - participant OpenList - participant 115 - OpenList->>115: Post refresh_token - 115->>OpenList: Get new access_token and refresh_token -``` - -::: -::: zh-CN +### 3.3. 当前AccessToken刷新的方式 ```mermaid sequenceDiagram @@ -267,26 +120,8 @@ sequenceDiagram 115服务器->>OpenList: 返回新的访问令牌+刷新令牌 ``` -## 4. Notes { lang="en" } - -## 4. 注意事项 { lang="zh-CN" } - -::: en -::: warning Handling Method for Token Leakage -If the Token is accidentally leaked, you can go to the 115 device login management to解除应用授权 (revoke application authorization). +## 4. 注意事项 -- 115 APP: [iOS, Android] versions need to be ≥ 35.11.0 -- 115 Web端: [https://115.com/?mode=device_manage](https://115.com/?mode=device_manage) - - When it expires, the following content will be prompted: - - ```json - failed get objs: failed to list objs: code: 40140116, message: no auth - ``` - -An account can obtain two `Refresh tokens` in the same application. After the third acquisition, the first obtained `Refresh token` will expire. Using the first `Refresh token` will prompt the above error. -::: -::: zh-CN ::: warning Token 泄漏后处理方法 如果不小心泄漏了 Token,可以前往115设备登录管理解除应用授权 @@ -302,61 +137,19 @@ An account can obtain two `Refresh tokens` in the same application. After the th 一个帐号可以在同一个应用获取两次`Refresh token`,第三次获取后第一次获取到的`Refresh token`就会失效,使用第一个`Refresh token`会提示上面的错误 ::: -## 5. Using Other APP IDs to Obtain Refresh Tokens (Not Yet Implemented) { lang="en" } - -## 5. 使用其他 APP ID 获取刷新令牌(尚未实现) { lang="zh-CN" } - -::: en -::: tip -Under development, the tutorial has not been updated yet. Stay tuned! -::: +## 5. 使用其他 APP ID 获取刷新令牌(尚未实现) -::: zh-CN ::: tip 开发中, 教程暂未更新, 敬请期待! ::: -## 6. Mobile QR Code Authorization PKCE Mode (Not Yet Implemented) { lang="en" } +## 6. 手机扫码授权PKCE模式(尚未实现) -## 6. 手机扫码授权PKCE模式(尚未实现) { lang="zh-CN" } - -::: en -::: tip -Under development, the tutorial has not been updated yet. Stay tuned! -::: - -::: zh-CN ::: tip 开发中, 教程暂未更新, 敬请期待! ::: -## 7. Default Download Method Used { lang="en" } - -## 7. 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 7. 默认使用的下载方式 ```mermaid --- @@ -376,5 +169,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/123.md b/pages/guide/drivers/123.md index 8d1a897ab..fe3e8f247 100644 --- a/pages/guide/drivers/123.md +++ b/pages/guide/drivers/123.md @@ -1,27 +1,14 @@ --- -title: - en: 123 Pan / Share / Link - zh-CN: 123 网盘 / 分享 / 直链 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 695 -# A page can have multiple categories categories: - guide - drivers --- - - -::: en -::: warning -123 Cloud Disk has implemented anti-leech measures for this **reverse-engineered driver**. To avoid unnecessary disputes, it is recommended that users choose the more standard 123 Open Platform driver. If you continue to use this driver, you need to enable a **local proxy** to prevent anti-leech detection. +# 123 网盘 / 分享 / 直链 -- [123 Open Platform](./123_open.md) + -::: -::: zh-CN ::: warning 123云盘对于该**逆向驱动**已经采取防盗链措施,为了避免不必要的纠纷,建议用户选用更为规范的123开放平台驱动。如果继续使用该驱动,需要开启**本地代理**以防止反盗链检测 @@ -29,32 +16,10 @@ categories: ::: -## Personal { lang="en" } - -## 个人 { lang="zh-CN" } - -::: en -Free user 1G traffic download, unlimited upload, multi-IP sharing is prohibited -::: +## 个人 -::: zh-CN 免费用户 1G 流量下载,上传不限制,禁止多IP共享使用 -::: -::: en -::: warning - -```json -failed get objs: failed to list objs:当前账号存在安全风险,请使用短信验证码或者微信进行登录。 -``` - -solution: - -- This is because the 123 cloud disk officially prohibits the mounting on strange devices, If you are mounted locally, `or` Windows Server server mount `to open the 123 cloud disk web page to log in or modify a password once - -::: - -::: zh-CN ::: warning 挂载提示: @@ -68,113 +33,39 @@ failed get objs: failed to list objs:当前账号存在安全风险,请使用 ::: -::: en -https://www.123pan.com/ - -Just fill in the account password. -::: - -::: zh-CN https://www.123pan.com/ 只需填写账户密码即可。 -::: - -### 用户名 { lang="zh-CN" } - -### Username { lang="en" } +### 用户名 -::: en -The mobile phone number used to log in -::: -::: zh-CN 用于登录的手机号码 -::: - -### 密码 { lang="zh-CN" } -### Password { lang="en" } +### 密码 -::: en -The password used to log in -::: -::: zh-CN 用于登录的密码 -::: -### 根文件夹 ID { lang="zh-CN" } +### 根文件夹 ID -### Root folder file_id { lang="en" } - -::: en -Enter the folder you want to mount, the last string of the official website URL, such as: -::: -::: zh-CN 输入要挂载的文件夹,官网 URL 的最后一串,如: -::: ![123](/img/drivers/123/123.png) -### 使用建议 { lang="zh-CN" } - -### Recommendations { lang="en" } - -::: en - -- It seems that the 123 API has a limited number of loads each time, so if you load hundreds of files in a folder at one time, an error may be reported -- It is recommended to A useful approach is not to put too many sub-items in the same folder. - -::: - -::: zh-CN +### 使用建议 - 貌似 123 的 API 每次加载的数量有限,故如果你一个文件夹内一次性加载几百个文件,可能会报错 - 建议不用在每个文件夹内放置太多子文件/文件夹 -::: - -## 直链 { lang="zh-CN" } - -## Link { lang="en" } +## 直链 -::: en -only adapted to the authentication function, the link also needs to be filled in by yourself, like [UrlTree](/guide/drivers/url_tree), fill in the direct link copied in 123 direct link. -::: -::: zh-CN 仅适配了鉴权功能,链接也需要自己填写,像[地址树一样](/guide/drivers/url_tree.md),把在 123 直链复制的直链填写进去即可 -::: - -::: en -::: danger Please read this reminder carefully -Since 123 Cloud Disk Direct Link is a paid service with a storage quota, if you decide to add more storage, please ensure you set up passwords, metadata, and other protective measures to prevent malicious traffic. - -After configuring these settings, test them in incognito mode to confirm everything is working correctly. If you fail to implement adequate anti-theft measures and experience malicious traffic overload, you will be held responsible for the consequences. -::: - -::: zh-CN ::: danger 请仔细阅读此提醒 由于123云盘直链是付费服务并有限制额度,如果您添加123直链存储,请及时设置密码、元信息等安全措施,以防止恶意刷流量。 设置完成后,请先在无痕模式下进行测试,确保防盗措施已正确设置。如未设置妥当,导致被恶意刷流量,后果自负。 ::: -::: en -First open **`123 Cloud Disk Direct Link Management`**: **https://www.123pan.com/DirectLink** Set an authentication key in the lower right corner, and then turn on the authentication status switch - -![123](/img/drivers/123/123_link_1.png) - -- Open Settings 123 Cloud Disk Settings: **https://www.123pan.com/Setting** and find the account ID [`1` label in the picture below] -- How to activate direct link? (You need to activate membership to use it) - - Find the folder you want to open, right-click on `Enable Direct Link Space`, and after opening it, you will see a link icon in the folder [Tag `2` in the picture below] -- How to get a direct link to a file? [Tag `3` in the picture below] - - Enter the folder that has 'enabled direct link space', find the file you need to obtain, right-click 'Get direct link', and fill in the OpenList configuration after obtaining it. - ![123](/img/drivers/123/123_link_2.png) - -::: - -::: zh-CN 首先打开 **`123云盘直链管理`**: **https://www.123pan.com/DirectLink** 右下角自己设置一个鉴权秘钥,然后打开鉴权状态开关 ![123](/img/drivers/123/123_link_1.png) @@ -186,42 +77,7 @@ First open **`123 Cloud Disk Direct Link Management`**: **https://www.123pan.com - 进入已经`启用直链空间`的文件夹,找到需要获取的文件右键`获取直链链接`,获取到后填写到OpenList配置內 ![123](/img/drivers/123/123_link_2.png) -::: - -### 填写示例 { lang="zh-CN" } - -### Fill in example { lang="en" } - -::: en - -- **Origin urls**:Fill in the direct link to the files we copied one by one - - It supports creating different folders like [UrlTree](/guide/drivers/url_tree), and supports filling in the file size and modification time (four options are provided for use, like the UrlTree filling, refer to the second example of the picture below) - - File size unit: `B` bytes. For example, if you want to write a 1MB file, it is 1048567 bytes. You can search for the specific byte conversion in your browser (you can leave it blank). - - File modification time: It is `Unix timestamp`. You can search in the browser for the specific conversion (you can leave it blank). - - Fill in the format: - - ```txt - [FileSize:][Modified:]URL - 127451136:1694101621:https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_mips64el.deb - [FileSize:]URL - 134847488:https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_loong64.deb - [Modified:]URL - 1694101621:https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_arm64.AppImage - URL - https://vip.123pan.cn/1812xxx499/123-link-Test/linuxqq_3.2.0-16736_x86_64.AppImage - ``` - -- **Private key**: - - You can set up the direct link management page yourself and turn it on. Please turn it on. -- **Uid**: - - Account ID in the account settings page -- **Valid duration**: - - The file direct link validity period, in minutes, is filled in by default to 30 minutes - -::: - -::: zh-CN +### 填写示例 - **来源链接**:填写我们一条一条复制的文件直链 - 支持像[地址数](/guide/drivers/url_tree)那样新建不同文件夹,支持填写文件大小和修改时间(提供四种方案可以使用,像地址树填写参考下方图片的图二示例) @@ -248,62 +104,24 @@ First open **`123 Cloud Disk Direct Link Management`**: **https://www.123pan.com - **有效期**: - 文件直链有效期,单位为分钟,默认填充为30分钟 -::: - ![123Link](/img/drivers/123/123_link_3.png) ![123Link](/img/drivers/123/123_link_4.png) -## 分享 { lang="zh-CN" } - -## Share { lang="en" } +## 分享 -::: en -Fill in the driver's **`share key`** and optional **`share password`** (if there is a password, you need to fill in), the root folder ID defaults to `0` to display all files -::: -::: zh-CN 填写驱动的 **`分享key`** 和选填 **`分享密码`** (如果有密码需要填写),根文件夹ID默认为`0`显示全部文件 -::: - -### 填写示例 { lang="zh-CN" } -### Fill in the example { lang="en" } +### 填写示例 -::: en ![123](/img/drivers/123/123_add_demo.png) -::: -::: zh-CN -![123](/img/drivers/123/123_add_demo.png) -::: - -### Share Password { lang="en" } -### 分享密码 { lang="zh-CN" } +### 分享密码 -::: en -Fill in if you have it, don't if you don't -::: -::: zh-CN 有就填写,没有就不用 -::: - -### Root folder file_id { lang="en" } -### 根文件夹 ID { lang="zh-CN" } +### 根文件夹 ID -::: en -The ID of the root directory of the shared link is `0`, displaying all files - -If you only want to display a certain folder, open the developer mode (F12) to clear all requests (maybe 123 prohibits debug debugging, you need to close this by yourself to continue) - -Find the request on the right side of the picture in the upper-level directory request of the directory you want to display, then click `Response`, find the format button `{}` below and format it, and you can see the relevant directory ID. - -If you are not sure whether the directory ID is correct or not, there is a directory name under the directory ID - -![123](/img/drivers/123/123_fl_id.png) - -::: -::: zh-CN 分享链接根目录ID是`0`,展示全部文件 如果只想展示某个文件夹,打开开发者模式(F12)清空全部请求(可能123禁止debug调试需要自己关闭这个才能继续) @@ -314,36 +132,7 @@ If you are not sure whether the directory ID is correct or not, there is a direc ![123](/img/drivers/123/123_fl_id.png) -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -363,5 +152,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/123_open.md b/pages/guide/drivers/123_open.md index 68ed98c2e..d94ff33f9 100644 --- a/pages/guide/drivers/123_open.md +++ b/pages/guide/drivers/123_open.md @@ -1,33 +1,18 @@ --- -title: - en: 123 Open - zh-CN: 123 开放平台 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 696 -# A page can have multiple categories categories: - guide - drivers --- +# 123 开放平台 + https://www.123pan.com/developer -## 1. Developer Application { lang="en" } - -## 1. 申请开发者 { lang="zh-CN" } - -::: en -::: warning -This driver uses the [developer authorization mode](https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/hpengmyg32blkbg8), which grants direct management access to the cloud drive associated with the provided public/private key pair, , so you **must use your client id and client secret**. - -The acquired token counts as a login device +## 1. 申请开发者 -::: -::: zh-CN ::: warning 该驱动使用的是[开发者授权模式](https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/hpengmyg32blkbg8),将会直接获得该密钥对应网盘的管理权限,所以**必须使用自己的密钥**。 @@ -35,21 +20,6 @@ The acquired token counts as a login device ::: -::: en - -**Application Method**: Visit the [123 Open Platform Official Website](https://www.123pan.com/developer), read the Developer Agreement, fill in the required fields marked with `*`, and apply for the `client_id` and `client_secret`.Typically, after your application is approved the keys will be sent to your email **remember to check your spam folder, and please keep the keys sent by email safe**. - -1. Sign the Developer Agreement - -2. Fill out the application materials - -3. Wait for the review notification - -**Reference Tutorial**: [OpenListTeam/discussions#55](https://github.com/orgs/OpenListTeam/discussions/55) - -::: -::: zh-CN - **申请方式**:访问[123开放平台官网](https://www.123pan.com/developer),阅读开发者协议,填写对应必填项`*`信息,申请`client_id`和`client_secret`,一般来说申请通过后会发送至邮箱,记得检查邮件的垃圾箱,**请保管好通过邮件发送回来的密钥**。 1. 签署开发者协议 @@ -60,31 +30,7 @@ The acquired token counts as a login device **参考教程**:[OpenListTeam/discussions#55](https://github.com/orgs/OpenListTeam/discussions/55) -::: - -### 2. Get UID { lang="en" } - -### 2. 获取 UID { lang="zh-CN" } - -::: en - -The method to obtain the "Cloud Drive UID" required during the application process is as follows: - -1. **Log in to the 123 Cloud Drive web platform** - - Visit the 123 Cloud Drive official website and log in with your account (phone number). - -2. **Go to the "Settings" page** - - After logging in, click on the profile picture or username at the top right, and select "Settings" (or directly visit: ). - -3. **Find the "Account ID"** - - In the "Account Settings" or "Security Settings" section, locate the "Account ID," which is your "Cloud Drive UID." Copy it and paste it into the application form. - -::: - -::: zh-CN +### 2. 获取 UID 填写申请过程中需要填入的“云盘UID”一项获取方式如下: @@ -100,61 +46,22 @@ The method to obtain the "Cloud Drive UID" required during the application proce 在「账号设置」或「安全设置」栏目中,找到“个人账号ID”,该ID即为您的“云盘UID”,复制并填入即可。 -::: - -## 4. 在 OpenList 中添加 { lang="zh-CN" } - -## 4. Add in OpenList { lang="en" } +## 4. 在 OpenList 中添加 -### 刷新令牌 { lang="zh-CN" } +### 刷新令牌 -### RefreshToken { lang="en" } - -::: zh-CN **留空** -::: -::: en -**keep it empty** -::: -### 客户端ID { lang="zh-CN" } +### 客户端ID -### Client ID { lang="en" } - -::: zh-CN 填入你的客户端ID -::: -::: en -Enter your client ID -::: - -### 客户端密钥 { lang="zh-CN" } -### Client Secret { lang="en" } +### 客户端密钥 -::: zh-CN 填入你的客户端密钥 -::: -::: en -Enter your client secret -::: - -### Root Folder ID { lang="en" } -### 根文件夹 ID { lang="zh-CN" } +### 根文件夹 ID -::: en -The default root directory ID is: `0` - -Open the official website of 123 Cloud Drive, navigate to the folder you want to set, and then click the number following `homeFilePath` in the URL. - -For example, - -API queries can also be used - -The `root folder ID` of this folder is `123456`. -::: -::: zh-CN 默认根目录ID为:`0` 打开 123 网盘官网,点击进入要设置的文件夹时点击 URL 中 `homeFilePath`后面的数字 @@ -167,39 +74,16 @@ The `root folder ID` of this folder is `123456`. 亦可右键文件夹,选择 `复制文件夹ID` -::: - -### Direct Link { lang="en" } - -### 使用直链 { lang="zh-CN" } +### 使用直链 -::: en -Disabled by default; returns standard download links. When enabled, returns CDN direct links, which require VIP access and will consume direct link traffic quota. - -Users must manually enable direct link space: Go to the 123 Cloud Drive official website, right-click a folder under the **root directory**, and select `Enable Direct Link Space (VIP)`. -::: -::: zh-CN 默认禁用,返回普通下载链接。启用后,返回 CDN 直链,需要开通 VIP,会消耗直链流量包。 需要用户手动启用直链空间,方法:进入 123 网盘官网,右键**根目录**下的文件夹,选择 `启用直链空间(VIP)`。 -::: ![](/img/drivers/123/123open-02.png) -### Direct Link Private Key { lang="en" } - -### 直链鉴权密钥 { lang="zh-CN" } - -::: en -Prerequisite: Enable `Direct Link`. - -Leave empty to disable direct link authentication and return permanent direct links. - -To prevent your site resources from being maliciously downloaded or stolen, you can configure an "Authentication Key" in 123 Cloud Drive's **Direct Link** → **Basic Function Configuration** → **URL Authentication**, and then set **Authentication Status** to **Enabled**. +### 直链鉴权密钥 -After entering the key, the obtained direct links will automatically include authentication parameters. -::: -::: zh-CN 前置条件:开启 `使用直链`。 默认为空,代表不启用直链鉴权,返回永久直链。 @@ -207,53 +91,16 @@ After entering the key, the obtained direct links will automatically include aut 为防止站点资源被恶意下载盗用,您可以在 123 云盘的 **直链** -> **基础功能配置** -> **URL鉴权** 中配置 **鉴权密钥**,然后将 **鉴权状态** 设为 **启用**。 填写密钥后,获取到的直链会自动加上鉴权参数。 -::: ![](/img/drivers/123/123open-03.png) -### Direct Link Valid Duration { lang="en" } - -### 直链鉴权有效期 { lang="zh-CN" } +### 直链鉴权有效期 -::: en -Prerequisite: Enable `Direct Link` and configure the `Direct Link Private Key`. - -Used to generate the expiration timestamp in the direct link authentication parameters. -::: -::: zh-CN 前置条件:开启 `使用直链`,配置 `直链鉴权密钥`。 用于生成直链鉴权参数中的过期时间戳。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[local proxy]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[302]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -273,5 +120,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/139.md b/pages/guide/drivers/139.md index 2e6970327..7acb94f51 100644 --- a/pages/guide/drivers/139.md +++ b/pages/guide/drivers/139.md @@ -1,73 +1,26 @@ --- -title: - en: 139Yun - zh-CN: 中国移动云盘 -# This is the icon of the page -icon: iconfont icon-state -# This control sidebar order top: 692 -# A page can have multiple categories categories: - guide - drivers --- - +# 中国移动云盘 -::: en -Cloud disk address: **** -::: + -::: zh-CN 云盘地址:**** -::: - -:::en -::: warning -The OpenList version must be greater than `v3.41.0` to use this tutorial. -::: -:::zh-CN ::: warning OpenList 版本必须大于 `v4.2.4` 才能使用本教程。 ::: -:::en -::: tip -Most parameters can be obtained from browser DevTools. See [Search keywords](#search-keywords). -::: - -:::zh-CN ::: tip 大部分参数可通过浏览器开发者工具获取。请参考[搜索关键词](#搜索关键词)。 ::: -## Quick start { lang="en" } +## 快速开始 -## 快速开始 { lang="zh-CN" } - -::: en -For long-term use, use the new personal cloud with password login fallback. This lets OpenList persist the generated `Authorization` and renew it automatically when possible. - -1. Log in to in your browser. -2. Copy cookies from `mail.10086.cn` and paste them into `MailCookies` as a Cookie Header String, for example `key1=value1; key2=value2`. Do not paste JSON, a table, or one cookie per line. -3. Fill your 139 email/mobile account in `Username`, and fill the corresponding password in `Password`. -4. Add a `139Yun` storage in OpenList and fill: - - `Type`: `personal_new` - - `MailCookies`: the cookies copied in step 2 - - `Username`: your account - - `Password`: your password - - `Root folder ID`: leave empty or fill `/` for the root directory -5. Leave `Authorization`, `Cloud ID`, and `UserDomainID` empty. -6. Save the storage. - -If you only want to mount quickly, you can fill only `Authorization`: log in to , find a `hcy/file/list` request in DevTools -> Network, copy the request header `Authorization`, and paste only the content after `Basic `. - -If you want to mount a subfolder, enter that folder on the 139Yun website first, then use the current `parentFileId` or `currentCatalogID` as `Root folder ID`. - -::: - -::: zh-CN 长期使用推荐按“新的个人盘 + 账号密码回退登录”配置。这样 OpenList 可以持久化生成的 “授权”,并在可行时自动续期。 1. 在浏览器登录 。 @@ -86,30 +39,8 @@ If you want to mount a subfolder, enter that folder on the 139Yun website first, 如果要挂载子文件夹,请先在移动云盘网页端进入该文件夹,再把当前的 `parentFileId` 或 `currentCatalogID` 填到 “根文件夹 ID”。 -::: - -## Authentication { lang="en" } - -## 鉴权方式 { lang="zh-CN" } +## 鉴权方式 -::: en -The driver supports three authentication methods. Use only one method unless you need password login as a fallback. - -| Method | Fields to fill | Notes | -| ----------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Password login fallback | `MailCookies`, `Username`, `Password` | Recommended for long-term use. `MailCookies` must be a Cookie Header String, such as `key1=value1; key2=value2`. The driver can generate and persist a new `Authorization`, and can fall back to password login when token refresh fails. | -| Authorization | `Authorization` | Fastest setup. Fill in the value after `Basic `. Do not include `Basic` itself. You may need to update it manually after it expires or refresh fails. | -| Mail cookies fast login | `MailCookies` | Use cookies from `mail.10086.cn`. `MailCookies` must be a Cookie Header String and contain valid key-value pairs; `Os_SSo_Sid` and `RMKEY` are used for fast login. | - -`Username`, `Password`, and `MailCookies` are not required when `Authorization` is valid, even if an old frontend marks them as required. - -When using password login fallback, `MailCookies`, `Username`, and `Password` must be filled together. - -`MailCookies` should look like the value of an HTTP `Cookie` request header: `key1=value1; key2=value2; key3=value3`. - -::: - -::: zh-CN 驱动支持三种鉴权方式。除非需要密码登录作为回退,否则选择其中一种即可。 | 方式 | 需要填写的字段 | 说明 | @@ -124,26 +55,8 @@ When using password login fallback, `MailCookies`, `Username`, and `Password` mu “邮箱 Cookie” 应该像 HTTP 请求头里的 `Cookie` 值:`key1=value1; key2=value2; key3=value3`。 -::: - -## Type { lang="en" } - -## 类型 { lang="zh-CN" } +## 类型 -::: en -OpenList currently supports five 139Yun storage types. `personal_new` is the default. - -| Type | Use for | Root folder ID when empty | Cloud ID | Notes | -| -------------- | ------------------------- | ------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------- | -| `personal_new` | New personal cloud | `/` | Not required | New API. Uses direct EOS multipart upload. | -| `family` | My Family -> Family Files | Automatically tries to save `data.path` without the `root:/` prefix | Required | Uses Family Cloud APIs. If auto-detection fails, fill the folder ID manually. | -| `group` | Shared group | Uses `Cloud ID` | Required | For groups created by others, manually fill the folder ID to avoid first-level folder loops. | -| `personal` | Old personal cloud | `root` | Not required | Legacy personal cloud. Most accounts have been migrated to `personal_new`. | -| `share` | Shared link mount | Share link ID from `LinkID` | Not required | Mount other users' shared content. Use `LinkID` to specify one or more share links. | - -::: - -::: zh-CN OpenList 目前支持五种中国移动云盘类型,默认类型是 `personal_new`(新的个人盘)。 | 类型 | 适用场景 | 根文件夹 ID 为空时 | Cloud ID | 说明 | @@ -154,35 +67,12 @@ OpenList 目前支持五种中国移动云盘类型,默认类型是 `personal_ | `personal`(个人云) | 个人云 | `root` | 不需要 | 个人云。多数账号已经迁移到 `personal_new`(新的个人盘)。 | | `share`(分享挂载) | 分享挂载 | 使用 `LinkID` 中的分享链接 ID | 不需要 | 挂载他人分享的内容。通过 `LinkID` 指定一个或多个分享链接。 | -::: - -:::en -::: warning -After changing `Type`, clear or update `Root folder ID`, then save the storage again. -::: - -:::zh-CN ::: warning 更改 `Type` 后,请清空或重新填写 `Root folder ID`,再保存存储。 ::: -## Root folder ID { lang="en" } - -## 根文件夹 ID { lang="zh-CN" } +## 根文件夹 ID -::: en -`Root folder ID` specifies the mounted directory. - -- `personal_new`: use `/` for the root. For a subfolder, use the folder ID from `parentFileId` or `currentCatalogID`. -- `family`: leave empty to let OpenList try to read `data.path` automatically. When filling manually, remove the `root:/` or `root:` prefix. -- `group`: leave empty only when mounting your own group root. For a subfolder or a group created by others, fill the folder ID manually. -- `personal`: use `root` for the legacy root. -- `share`: not used. The share link ID from `LinkID` replaces the root folder concept. - -Do not add extra `/` around subfolder IDs. For example, use `abc123`, not `/abc123`. -::: - -::: zh-CN “根文件夹 ID” 用于指定要挂载的目录。 - `personal_new`(新的个人盘):根目录填写 `/`。挂载子文件夹时,使用 `parentFileId` 或 `currentCatalogID` 中的文件夹 ID。 @@ -193,23 +83,8 @@ Do not add extra `/` around subfolder IDs. For example, use `abc123`, not `/abc1 手动填写子文件夹 ID 时不要额外添加 `/`。例如填写 `abc123`,不要填写 `/abc123`。 -::: - -## Cloud ID { lang="en" } - -## Cloud ID { lang="zh-CN" } - -::: en -`Cloud ID` is required only for `family` and `group`. - -- `family`: family cloud ID. -- `group`: group ID. -- `personal_new` and `personal`: leave empty. -- `share`: leave empty. - -::: +## Cloud ID -::: zh-CN Cloud ID 只在 `family`(家庭云) 和 `group`(共享群) 类型下需要填写。 - `family`(家庭云):家庭云 ID。 @@ -217,86 +92,25 @@ Cloud ID 只在 `family`(家庭云) 和 `group`(共享群) 类型下需 - `personal_new`(新的个人盘) 和 `personal`(个人云):留空。 - `share`(分享挂载):留空。 -::: - -## User domain ID { lang="en" } - -## 用户域 ID { lang="zh-CN" } - -::: en -`UserDomainID` is the `ud_id` value in cookies. It is optional for mounting and is mainly used to show disk usage in storage details. If it is empty, file listing and downloads can still work, but capacity statistics are unavailable. +## 用户域 ID -::: - -::: zh-CN “用户域 ID” 是 Cookie 中的 `ud_id`。它不是挂载必填项,主要用于在存储详情中显示容量统计。留空时仍可列目录和下载,但无法显示容量信息。 -::: - -## Advanced options { lang="en" } - -## 高级选项 { lang="zh-CN" } - -::: en - -- `Custom upload part size`: upload part size in bytes. `0` means automatic. The driver uses `100 MB` by default and increases it to `512 MB` for files larger than `30 GB`. -- `Report real size`: enabled by default. For old personal, family, and group uploads, it reports the real file size to the upstream API. -- `Use large thumbnail`: disabled by default. Enable it to prefer large image thumbnails when the new personal cloud API returns them. -- `Use old stream upload`: disabled by default. Enable it to use the legacy streaming upload method for family and group cloud types. The new method supports rapid upload (server-side duplicate check) but does not support streaming; the legacy method does not support rapid upload. - -::: - -::: zh-CN +## 高级选项 - 自定义上传分片大小:上传分片大小,单位为字节。`0` 表示自动。驱动默认使用 `100 MB`,文件大于 `30 GB` 时会自动使用 `512 MB`。 - 报告真实大小:默认开启。个人云、家庭云、共享群上传时,会向上游接口上报真实文件大小。 - 使用大缩略图:默认关闭。开启后,新的个人盘接口返回大图缩略图时会优先使用大图。 - 使用旧版流式上传:默认关闭。开启后,家庭云和共享群使用旧版流式上传方式。新版上传支持秒传(服务端去重)但不支持流式;旧版上传不支持秒传。 -::: - -## Proxy Range { lang="en" } - -## 代理 Range { lang="zh-CN" } - -::: en -`Proxy Range` is enabled by default in the driver, but it only takes effect after enabling `Web Proxy` or `WebDAV Native Proxy`. - -Enable proxy mode when a player or downloader cannot handle the upstream 302 link correctly, for example when video playback fails, seeking fails, or resumable downloads do not work. +## 代理 Range -::: - -::: zh-CN 驱动默认开启 `代理 Range`,但它需要配合 `Web 代理` 或 `WebDAV 本地代理` 才会生效。 当播放器或下载器不能正确处理上游 302 链接时,建议开启代理模式,例如视频无法播放、拖动进度失败或不支持断点续传。 -::: - -## Search keywords { lang="en" } - -## 搜索关键词 { lang="zh-CN" } +## 搜索关键词 -::: en -Use browser DevTools to find the fields below. - -| Need | Where to search | Field | -| ------------------------- | --------------------------------------------------- | -------------------------------------------------------------- | -| `Authorization` | Network request headers on `yun.139.com` | `Authorization: Basic ...`; copy only the value after `Basic ` | -| New personal folder ID | `hcy/file/list` request, or browser storage | `parentFileId` or `currentCatalogID` | -| Family Cloud ID | `queryContentList` request payload | `cloudID` | -| Family folder ID | `queryContentList` response | `data.path`; remove `root:/` or `root:` when filling manually | -| Group ID | `queryGroupContentList` request payload | `groupID` | -| Group folder ID | `queryGroupContentList` request payload or response | `path` or folder ID | -| Legacy personal folder ID | `getDisk` request or response | `catalogID` | -| `UserDomainID` | Browser cookies | `ud_id` | -| Share link ID | Share page URL on `yun.139.com` | The path segment after `/share/` in the sharing URL | - -In Firefox, cookies and local storage can be easier to find in DevTools -> Storage. - -::: - -::: zh-CN 使用浏览器开发者工具查找以下字段。 | 需要获取 | 查找位置 | 字段 | @@ -313,23 +127,8 @@ In Firefox, cookies and local storage can be easier to find in DevTools -> Stora Firefox 中可以在开发者工具 -> 存储中查看 Cookie 和本地存储,通常更容易找到 `Authorization`、`ud_id` 和 `currentCatalogID`。 -::: - -### Personal new { lang="en" } - -### 新的个人盘 { lang="zh-CN" } - -::: en -Choose one of the following methods to find `Authorization` and folder ID. - -![](/img/drivers/139/new_personal.png) -![](/img/drivers/139/139_new_au.png) - -If you want the folder ID of a subfolder, enter that subfolder first and then inspect the new request or storage value. Otherwise, the old folder ID may still be displayed. - -::: +### 新的个人盘 -::: zh-CN 以下方法可用于查找 “授权” 和文件夹 ID。 ![](/img/drivers/139/new_personal.png) @@ -337,30 +136,13 @@ If you want the folder ID of a subfolder, enter that subfolder first and then in 如果要查看子文件夹 ID,请先进入该子文件夹,再查看新的请求或存储值,否则看到的可能仍是之前的文件夹 ID。 -::: - -### Personal cloud { lang="en" } - -### 个人云 { lang="zh-CN" } +### 个人云 ![](/img/drivers/139/other-personal.png) ![](/img/drivers/139/ch-personal.png) -### Family cloud { lang="en" } - -### 家庭云 { lang="zh-CN" } +### 家庭云 -:::en -![](/img/drivers/139/other-family.png) -![](/img/drivers/139/ch-family.png) - -::: details Teaching video -Although the video is for V2, the method for obtaining folder ID and Cloud ID is still similar. - -**** -::: - -:::zh-CN ![](/img/drivers/139/other-family.png) ![](/img/drivers/139/ch-family.png) @@ -370,21 +152,7 @@ Although the video is for V2, the method for obtaining folder ID and Cloud ID is **** ::: -### OpenList fill in examples { lang="en" } - -### OpenList 挂载填写示例 { lang="zh-CN" } - -::: en - -- `Authorization`: fill in only the content after `Basic `. -- New personal folder ID: enter the target folder first, then use the current `currentCatalogID`. - -![](/img/drivers/139/add-personal.png) -![](/img/drivers/139/add-family.png) -![](/img/drivers/139/add_new_personal.png) -::: - -::: zh-CN +### OpenList 挂载填写示例 - “授权”:只填写 `Basic ` 后面的内容。 - 新的个人盘文件夹 ID:先进入目标文件夹,再使用当前的 `currentCatalogID`。 @@ -392,83 +160,28 @@ Although the video is for V2, the method for obtaining folder ID and Cloud ID is ![](/img/drivers/139/add-personal.png) ![](/img/drivers/139/add-family.png) ![](/img/drivers/139/add_new_personal.png) -::: - -## Share mount { lang="en" } -## 分享挂载 { lang="zh-CN" } - -::: en -The `share` type allows you to mount shared content from other 139Yun users. You need a valid authorization to access download APIs. -::: +## 分享挂载 -::: zh-CN `share`(分享挂载)类型允许你挂载其他移动云盘用户分享的内容。访问下载接口仍需要有效的鉴权。 -::: ### LinkID format ### LinkID 格式 -::: en -`LinkID` supports the following formats: - -- Single share: directly enter the share link ID -- Password-protected share: `link_id#password` -- Multiple shares: separate multiple entries with commas or new lines. For example: `share_a,share_b,share_c` or `share_a#pass1,share_b` - -::: - -::: zh-CN `LinkID`(分享链接 ID)支持以下格式: - 单个分享:直接填写分享链接 ID - 加密分享:`link_id#password`(链接 ID 后加 `#` 和密码) - 多个分享:多个分享用逗号或换行分隔,例如:`share_a,share_b,share_c` 或 `share_a#密码1,share_b` -::: - -### Get share link ID { lang="en" } - -### 获取分享链接 ID { lang="zh-CN" } - -::: en - -1. Open the share link in your browser, e.g. `https://yun.139.com/w/#/share/xxxxx` -2. The path segment after `/share/` is the share link ID (e.g. `xxxxx`) -3. If the share is password-protected, append `#password` after the link ID - -::: - -::: zh-CN +### 获取分享链接 ID 1. 在浏览器中打开分享链接,例如 `https://yun.139.com/w/#/share/xxxxx` 2. 链接中 `/share/` 后面的路径段就是分享链接 ID(如上例中的 `xxxxx`) 3. 如果分享有密码保护,在链接 ID 后加 `#密码` -::: - -### Configuration { lang="en" } - -### 配置说明 { lang="zh-CN" } - -::: en - -- **Type**: `share` -- **LinkID**: one or more share link IDs (see format above) -- **Authorization** or **MailCookies + Username + Password**: required for authentication (same as other types) -- **Root folder ID**: not used for share type. The share link ID in `LinkID` determines the mounted content. -- **Cloud ID**: leave empty - -Notes: - -- Upload operations are not supported for the `share` type. -- When mounting multiple shares, the root directory shows each share as a separate folder. -- `${data_path}` resolves to the share root for path-based operations. - -::: - -::: zh-CN +### 配置说明 - **类型**:`share`(分享挂载) - **LinkID**:一个或多个分享链接 ID(格式见上方) @@ -482,37 +195,8 @@ Notes: - 挂载多个分享时,根目录会以文件夹形式展示每个分享的内容。 - path 相关操作会以分享根目录作为 `${data_path}`。 -::: +## 下载方式 -## Download method { lang="en" } - -## 下载方式 { lang="zh-CN" } - -::: en -The default download method is 302 redirection. If a player such as PotPlayer cannot play through 302, switch that mount to proxy mode or mount it through WebDAV with `WebDAV Native Proxy`. - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN 默认下载方式是 302 跳转。如果 PotPlayer 等播放器无法通过 302 播放,可以将该挂载切换为代理模式,或通过启用 `WebDAV 本地代理` 的 WebDAV 挂载播放。 ```mermaid @@ -533,5 +217,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/163music.md b/pages/guide/drivers/163music.md index e55cc21d7..82f3e1b46 100644 --- a/pages/guide/drivers/163music.md +++ b/pages/guide/drivers/163music.md @@ -1,40 +1,18 @@ --- -title: - en: 163 Music Drive - zh-CN: 网易云音乐云盘 -icon: iconfont icon-state -# This control sidebar order top: 677 -# A page can have multiple categories categories: - guide - drivers --- +# 网易云音乐云盘 + -::: en -163 Music Drive can only upload music and other operations on the mobile phone, and the web page cannot operate -::: -::: zh-CN 网易云音乐云盘需要在手机端才可以进行上传音乐等操作,网页端无法操作 -::: - -## Cookie { lang="en" } - -## Cookie { lang="zh-CN" } - -::: en -Open **https://music.163.com** and then open the F12 developer mode - -Open a request at will, just carry the `Cookie` to find the following two parameters and fill in the OpenList` Cookie` - -- **\_\_csrf** 、**MUSIC_U** - ![](/img/drivers/163/163_cookie.png) -::: +## Cookie -::: zh-CN 进入 **https://music.163.com** 然后打开F12开发者模式 随意打开一个请求只要携带 `Cookie` 找到以下两个参数填写进OpenList的 `Cookie` 中就可以使用 @@ -42,64 +20,16 @@ Open a request at will, just carry the `Cookie` to find the following two parame - **\_\_csrf** 、**MUSIC_U** ![](/img/drivers/163/163_cookie.png) -::: +## 歌曲获取上限 -## Song Limit { lang="en" } - -## 歌曲获取上限 { lang="zh-CN" } - -::: en -You can only get 200 songs by default. If you exceed you, you can modify this option -::: -::: zh-CN 默认只能获取200首歌曲,如果你超出可以修改此选项 -::: - -### Precautions { lang="en" } - -### 注意事项 { lang="zh-CN" } -::: en - -1. If there are many list files, it is recommended to open pagination loading, otherwise it will not be loaded -2. Cloud space can only upload audio format files, and cannot upload other format files - -::: - -::: zh-CN +### 注意事项 1. 如果列表文件很多,建议开启分页加载 2. 云空间只可以上传音频格式文件,无法上传其它格式文件 -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -119,5 +49,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/189.md b/pages/guide/drivers/189.md index e84429d70..b400f0850 100644 --- a/pages/guide/drivers/189.md +++ b/pages/guide/drivers/189.md @@ -1,43 +1,22 @@ --- -title: - en: 189Cloud - zh-CN: 电信天翼云盘 -icon: iconfont icon-state -# This control sidebar order top: 694 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - - -::: en -::: tip -The web -side login has been replaced with sliding verification code, **no longer supports OCR or manual input**. If the verification code needs to be used, please use the add `Cookie` to log in +# 电信天翼云盘 -If you encounter "Device ID does not exist, secondary device verification required",Open the 189 Account website at , log in, and then disable the Device Lock.. + -::: zh-CN ::: tip Web 端登录已更换为滑动验证码,**不再支持 OCR 与手动输入**,若需要验证码请使用 `添加 Cookie 进行登录` 或使用 `天翼云盘客户端` 驱动 如遇 **设备 ID 不存在,需要二次设备校验**,打开天翼账号网站 ,登陆后关掉设备锁即可 ::: -## 189CloudTV { lang="zh-CN" } +## 189CloudTV -::: zh-CN 使用天翼网盘的TV接口,挂载步骤最少。 (部分用户反馈存在限速问题,如果播放视频卡顿黑屏等无法加载的现象请改用天翼网盘客户端) @@ -52,69 +31,18 @@ Web 端登录已更换为滑动验证码,**不再支持 OCR 与手动输入** 选择短信登录 -::: - -## 189CloudTV { lang="en" } - -::: en -Uses the TV interface of 189 Cloud Drive, with the fewest mounting steps. - -(Some users have reported throttling issues. If you experience video playback stuttering, black screens, or failure to load, please switch to the 189 Cloud PC client.) - -1. When mounting, select 189CloudTV. Leave the login parameters blank. If you are unsure, **just fill in the mount path**. - -2. After clicking save, simply return to the storage management page. You can choose to log in by scanning a QR code or click the link to log in (for link login, if you are unsure, it is recommended to **right-click the link and open it in a new window**). +## 个人云 - Choose link or QR code +### 用户名 -3. After entering, select SMS login. Once logged in, return to the storage management page. Disable and re-enable the storage to use it normally (you may need to refresh the page). - - Select SMS login - -::: - -## 个人云 { lang="zh-CN" } - -## Personal Cloud { lang="en" } - -### Username { lang="en" } - -### 用户名 { lang="zh-CN" } - -::: en -the phone number used to log in -::: -::: zh-CN 用于登录的电话号码 -::: - -### 密码 { lang="zh-CN" } -### Password { lang="en" } +### 密码 -::: en -password for login -::: - -::: zh-CN 登录密码 -::: -### 根文件夹ID { lang="zh-CN" } - -### Root folder ID { lang="en" } - -::: en -The string at the end of the official website url, such as: - -- https://cloud.189.cn/web/main/file/folder/-11 -> `-11` -- https://cloud.189.cn/web/main/file/folder/71398114617385472 -> `71398114617385472` +### 根文件夹ID - ![189](/img/drivers/189/189.png) - -::: - -::: zh-CN 官网 URL 末尾的字符串,如: - https://cloud.189.cn/web/main/file/folder/-11 -> `-11` @@ -122,113 +50,39 @@ The string at the end of the official website url, such as: ![189](/img/drivers/189/189.png) -::: - -### 家庭云中转 { lang="zh-CN" } - -### Family transfer { lang="en" } - -::: en -Give 189 Cloud adds Personal's `Family Transfer option`, which is convenient for users without VIP, and a large number of family cloud spaces upload. - -- Note: The old upload interface family cloud will still limit the upload quantity, so `Rapid upload` and ` Old Upload` will not take effect +### 家庭云中转 -::: -::: zh-CN 为天翼云盘增加个人云使用`家庭云中转选项`,方便不开会员且家庭云空间小情况下大量上传。 - 注:旧的上传接口家庭云依然会限制上传量,所以`秒传选项`和`旧的上传方式`不生效 -::: - -## 家庭云 { lang="zh-CN" } - -## Family Cloud { lang="en" } - -::: en -(189 Cloud PC Driver Only) Use a computer browser, open the developer tool (F12), switch the emulation device and select the mobile device +## 家庭云 -Open https://h5.cloud.189.cn/main.html#/family, enter the folder you want to mount, you can see the request in the network, and then find the required parameters: -::: - -::: zh-CN (天翼云盘客户端驱动专用)使用电脑浏览器,打开开发者工具(F12),切换仿真设备选择手机设备 打开https://h5.cloud.189.cn/main.html#/family ,进入你想挂载的文件夹,可在网络中看到请求,然后找到所需参数: -::: ![189](/img/drivers/189/189-1.png) -### OpenList挂载填写示例: { lang="zh-CN" } - -### OpenList fill in examples: { lang="en" } - -#### 天翼云盘 { lang="zh-CN" } +### OpenList挂载填写示例: -#### 189 Cloud { lang="en" } +#### 天翼云盘 -::: en -Fill in the account1and password2,Then click one request in the request, just bring `Cookies`3, click on one at will Then fill in,Cookie expires time is unknown -::: - -::: zh-CN 填写帐号1和密码2,然后在请求中随便点击一个请求随意点击一个携带`Cookie`3的参数复制填写,Cookie有效期未知。 -::: ![189](/img/drivers/189/189-0.png) -#### 天翼云盘客户端 { lang="zh-CN" } - -#### 189 CloudPC { lang="en" } +#### 天翼云盘客户端 ![189](/img/drivers/189/189-2.png) -::: en -Video reference: **https://www.bilibili.com/video/BV16A4y197De** -::: -::: zh-CN 视频参考:**https://www.bilibili.com/video/BV16A4y197De** -::: - -## 建议 { lang="zh-CN" } -## suggestion { lang="en" } +## 建议 -::: en -It is recommended to use the 189 Cloud PC first, [**Notes click to view.**](../../faq/howto.md#when-adding-a-189-cloud-storage-the-device-id-does-not-exist-and-a-secondary-device-verification-is-required) -::: -::: zh-CN 建议首选使用天翼云盘客户端,[**注意事项点击查看**](../../faq/howto.md#添加-天翼云盘-云存储时-设备-id-不存在-需要二次设备验证) -::: -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -248,5 +102,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/alias.md b/pages/guide/drivers/alias.md index 74f94447f..8378c126c 100644 --- a/pages/guide/drivers/alias.md +++ b/pages/guide/drivers/alias.md @@ -1,22 +1,10 @@ --- -title: - en: Alias - zh-CN: 别名(Alias) -icon: iconfont icon-state -# This control sidebar order top: 997 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 别名(Alias) + diff --git a/pages/guide/drivers/alias_text.md b/pages/guide/drivers/alias_text.md index 86c6c72da..5a6894cde 100644 --- a/pages/guide/drivers/alias_text.md +++ b/pages/guide/drivers/alias_text.md @@ -1,35 +1,3 @@ -::: en - -**Alias** is a feature that allows multiple different cloud drives or storage paths to be merged into a unified directory. By combining paths, content from different cloud drives or folders can be displayed in the same interface, simplifying access and management. - -For example: Cloud Drive Account 1 and Cloud Drive Account 2 both contain a folder named `Movies`, but the contents of these folders may not be identical. - -- **Previously (Virtual Path)**: - - You could only mount them to two different paths separately, like: - - `CloudDrive1/Movies`, `CloudDrive2/Movies` - - `Movies/CloudDrive1`, `Movies/CloudDrive2` - -- **Now (Alias)**: - - An aggregated folder (Movies) is provided, which can contain content from both Cloud Drive 1 and Cloud Drive 2 simultaneously. - -Folders with the same name will be automatically merged into one. The contents of the folder are the sum of the contents of all folders with the same name. Regarding how this driver handles files with the same name, please refer to the [Path conflict policies](/guide/drivers/alias#path-conflict-policies) section later. - -For Example: - -![alias](/img/drivers/alias/alias.png) - -In the diagram, we can see that two different folders are merged into one. Files and folders with the same name are also combined, and unique ones are displayed separately. - -Example explanations: - -- **Example 1**: `riluo.jpg` is unique to Cloud Drive 1, so it is shown separately. -- **Example 2**: Both folders contain a `video` folder, but the contents of these folders will be merged. Subfolders also follow the **same-name merging** display rule. Both folders contain two videos, but one of them has the same name. After merging according to the **same-name merging** rule, three video files will be displayed in total. - -::: -::: zh-CN - **别名(Alias)** 是一种将多个不同云盘或存储路径合并为一个统一目录的功能。通过路径合并,来自不同云盘或文件夹的内容可以在同一界面中展示,从而简化访问和管理。 例如:云盘账号1和云盘账号2中都包含一个名为`电影`的文件夹,但两者的内容未必完全相同。 @@ -57,39 +25,8 @@ Example explanations: - **例1**:`riluo.jpg` 仅存在于云盘1中,因此显示为独有文件。 - **例2**:两个文件夹都有一个名为`video`的子文件夹。合并后,两个`video`文件夹的内容会合并展示。若其中有同名文件(例如`movie.mp4`),合并后最终展示3个视频文件。 -::: - -## Paths filling method { lang="en" } +## 路径填写方式 -## 路径填写方式 { lang="zh-CN" } - -::: en -There are two ways to fill in: - -1. The first one is that you can only fill in the path of the subfolder and the folder with the same name must be used at the end. It is not recommended to use :x: - - Paths filling example: - ``` - /file1/locala - /file2/localb - ``` -2. The second is to directly mount the root folder path, using the `renaming` method, it is strongly recommended to use :heavy_check_mark: - - Paths filling example: - - ``` - #Example 1 Directly write the root folder - merge: /file1 - merge: /file2 - - #Example 2 Mount different path folders for merging - merge: /file1/localtest233 - merge: /file2/videos/TV series - merge: /file3 2/TV/Domestic TV Series/Station XX - ``` - -According to the second method, we can `merge` and display different folders, which is convenient and quick. -::: - -::: zh-CN 填写方式有两种: 1. 第一种是只可以填写子文件夹路径并且最后结尾时必须同名文件夹才可以,不推荐使用 :x: @@ -113,91 +50,13 @@ According to the second method, we can `merge` and display different folders, wh ``` 根据第二种这样我们可以将不同的文件夹进行合并显示,方便快捷。 -::: - -## Path conflict policies { lang="en" } -## 路径冲突策略 { lang="zh-CN" } +## 路径冲突策略 -::: en -The three configuration items—**Reading conflict policy**, **Writing conflict policy**, and **Putting conflict policy**—determine how the Alias handles files or folders with the same name and identical paths in the backend drivers. Their values and corresponding behaviors are as follows: - -::: -::: zh-CN **读取冲突策略**、**写入冲突策略**和**上传冲突策略**三个配置项确定了别名驱动如何处理后端驱动中路径相同的同名文件或文件夹,其值与对应的行为如下: -::: - -### Reading conflict policy { lang="en" } - -### 读取冲突策略 { lang="zh-CN" } - -::: en -Determines the behavior for handling files with the same name when downloading, copying (as source files), and extracting (as source files) (excluding moving). - -- Get the file corresponding to the first conflict path: Select the file from the first available path (from top to bottom in the Paths) where the file exists. - -- Load balancing on a per-file basis: Randomly select one file from the available duplicates. +### 读取冲突策略 -- Load balancing on a per-part basis: - - During 302 downloads, copying, or extracting, it falls back to the **Load balancing on a per-file basis** strategy. - - When using local proxy downloads, each transmitted chunk is randomly assigned to one of the duplicate files. This requires the backend driver to support Range requests. For details on setting the chunk size, refer to the section [Download concurrency, Download part size](/guide/drivers/alias#download-concurrency-download-part-size). - - This policy achieves the actual effect as shown in the figure. It is important to note that this diagram is only intended to conveniently demonstrate the operation of this policy. The statement that "participating load-balanced files with the same name have different contents" is **not** a correct usage of this driver. OpenList does not guarantee stable or correct results under such circumstances, nor will it address any issues arising from this usage scenario. Furthermore, the actual minimum size for file splitting is 1 KiB, not 1 byte, so the effect depicted in the diagram will not occur in the stable version. - - ![](/img/drivers/alias/read_policy_all.png) - -::: tip -When a copy operation involves multiple source paths and target paths, the Alias will first attempt to pair source and target paths that belong to the same drive. Only for target paths that cannot be matched in this way will the method specified by the **Reading conflict policy** be used to select a source path. - -For example, when copying: - -- `DriverA/source/file.txt` -- `DriverB/source/file.txt` - -to: - -- `DriverB/target/` -- `DriverA/target/` -- `DriverC/target/` - -The Alias driver will perform the following operations: - -1. Copy `DriverA/source/file.txt` to `DriverA/target/`, since both are located in Driver A. -2. Copy `DriverB/source/file.txt` to `DriverB/target/`, since both are located in Driver B. -3. For `DriverC/target/`, which has no matching source in the same driver, select `file.txt` from either Driver A or Driver B according to the **Reading conflict policy**, and copy it to `DriverC/target/` via an upload operation (creating a copy task). - -::: -::: en -::: tip -The move operation involves a matching process similar to that of the copy operation. For target paths that cannot be matched with a source path from the same driver, the Alias will randomly select from the still-unmatched source paths to create a pairing (**one-to-one correspondence**). If the number of source paths exceeds the number of target paths, the unmatched source paths will be deleted. If the number of source paths is less than the number of target paths, the move operation will fail. The matching behavior for the move operation is not affected by the **Reading conflict policy**. - -For example, in the case described for the copy operation, since there is no source path corresponding to `DriverC/target/`, the move operation will fail. - -As another example, when moving: - -- `DriverA/source/file.txt` -- `DriverB/source/file.txt` -- `DriverD/source/file.txt` -- `DriverE/source/file.txt` -- `DriverG/source/file.txt` - -to: - -- `DriverA/target/` -- `DriverB/target/` -- `DriverC/target/` -- `DriverF/target/` - -The Alias will perform the following operations: - -1. Move `DriverA/source/file.txt` to `DriverA/target/`. -2. Move `DriverB/source/file.txt` to `DriverB/target/`. -3. Randomly select one file from Driver D, E, or G, and move it to `DriverC/target/` (by creating a move task, which is essentially an upload followed by deletion). Let's assume E is selected. -4. Randomly select one file from the remaining unmatched drivers (D or G), and move it to `DriverF/target/`. Let's assume G is selected. -5. Delete `DriverD/source/file.txt`. - -::: -::: zh-CN 影响下载、复制(作为源文件)、解压(作为源文件)(没有移动)同名文件的方式。 - 读取首个有效路径:选择填写的所有路径中从上到下第一个存在该文件的路径中的同名文件。 @@ -230,7 +89,7 @@ The Alias will perform the following operations: 3. 按照**读取冲突策略**给出的方案选择驱动 A 或 B 中的`file.txt`,通过上传的方式(创建复制任务)复制到`驱动C/target/`。 ::: -::: zh-CN + ::: tip 移动操作存在与复制操作类似的匹配过程,对于未获得匹配的目标路径,随机选择同样未被匹配的源路径进行匹配(**一一对应**),源路径数量超过目标路径时,未得到匹配的源路径会被删除,源路径数量少于目标路径时,移动操作会失败。移动操作的匹配行为不会受到**读取冲突策略**的影响。 @@ -261,54 +120,8 @@ The Alias will perform the following operations: ::: -### Writing conflict policy { lang="en" } - -### 写入冲突策略 { lang="zh-CN" } - -::: en -Determines the behavior for renaming or deleting files/folders with the same name, as well as creating folders within identically named folders. - -- **Disable writing**: Prohibits rename, delete, and folder creation operations. -- **Write into the first conflict path**: Operates on the driver containing the target path that appears first (top to bottom) in the configured path list. -- **Allow unique path**: Executes the operation only if the target path is unique (exists in only one backend path). Otherwise, the operation is prohibited. -- **Allow full conflict paths**: Executes the operation only if the target path exists in **all** configured backend paths, applying the operation across all of them. Otherwise, the operation is prohibited. -- **Allow unique path and full conflict paths**: Allows the operation when the target path is either unique or exists in all configured backend paths. -- **Write into all conflict paths**: Forwards the operation to all backend paths where the target path exists. - -::: tip -If the above explanation is unclear, you can refer to the following example. - -- In Driver A, the file exists: `file1.txt` -- In Driver B, the files exist: `file1.txt`, `file2.txt`, `file3.txt` -- In Driver C, the files exist: `file1.txt`, `file3.txt` - -Configured backend paths: - -``` -test:DriverA -test:DriverB -test:DriverC -``` - -Then: - -- Since the sub-path `/file1.txt` is valid in all configured paths (`DriverA/`, `DriverB/`, `DriverC/`), `/file1.txt` is referred to as a **full conflict path**. -- Since the sub-path `/file2.txt` is valid only under the single backend path `DriverB/`, `/file2.txt` is referred to as a **non-conflict path** or **unique path**. -- Since the sub-path `/file3.txt` exists under both `DriverB/` and `DriverC/`, and the number of backend paths where it exists is neither 1 nor the maximum (3), `/file3.txt` is neither a non-conflict path nor a full conflict path. +### 写入冲突策略 -When renaming any file to `file4.txt`, the corresponding files in the following drivers will be renamed: - -| Writing conflict policy | file1.txt | file2.txt | file3.txt | -| ----------------------------------------- | ---------------------------- | --------- | ------------------ | -| Disable writing | Fails | Fails | Fails | -| Write into the first conflict path | Driver A | Driver B | Driver B | -| Allow unique path | Fails | Driver B | Fails | -| Allow full conflict paths | Driver A, Driver B, Driver C | Fails | Fails | -| Allow unique path and full conflict paths | Driver A, Driver B, Driver C | Driver B | Fails | -| Write into all conflict paths | Driver A, Driver B, Driver C | Driver B | Driver B, Driver C | - -::: -::: zh-CN 影响重命名、删除同名文件或文件夹,在同名文件夹下创建文件夹的方式。 - 禁用:禁止重命名、删除和创建文件夹操作。 @@ -340,49 +153,20 @@ test:驱动C - 由于子路径`/file3.txt`在`驱动B/`和`驱动C/`下存在,存在的后端路径数量既不为 1,也不为最大(3),因此`/file3.txt`既不是不冲突路径,也不是全冲突路径。 重命名任意文件为`file4.txt`时,以下驱动中的对应文件会被重命名: -| 写入冲突策略 | file1.txt | file2.txt | file3.txt | -| -- | -- | -- | -- | -| 禁用 | 操作失败 | 操作失败 | 操作失败 | -| 写入首个有效路径 | 驱动A | 驱动B | 驱动B | -| 仅允许不冲突路径 | 操作失败 | 驱动B | 操作失败 | -| 仅允许全冲突路径 | 驱动A、驱动B、驱动C | 操作失败 | 操作失败 | -| 允许不冲突路径和全冲突路径 | 驱动A、驱动B、驱动C | 驱动B | 操作失败 | -| 写入所有有效路径 | 驱动A、驱动B、驱动C | 驱动B | 驱动B、驱动C | - -::: - -### Putting conflict policy { lang="en" } - -### 上传冲突策略 { lang="zh-CN" } - -::: en -Determines the behavior for uploading to, copying to, moving to, or extracting to folders with the same name. - -- **Disable putting**, **Put into the first conflict path**, **Allow unique path**, **Allow full conflict paths**, **Allow unique path and full conflict paths**, **Put into all conflict paths**: These options function identically to their counterparts in the **Writing conflict policy**. -- **Random load balancing**: Randomly selects one valid path for the upload. -- **Weighted random load balancing based on remaining space**: Retrieves the remaining free space of all valid paths, skips paths that fail to report free space or have insufficient space for the file being uploaded, and then randomly selects from the remaining valid paths, weighting the choice by their remaining capacity. If none of the valid paths that successfully reported free space have enough capacity for the file, **a random selection is made from among the valid paths that failed to report capacity**. -- **Strict weighted random load balancing based on remaining space**: Retrieves the remaining free space of all valid paths, skips paths that fail to report free space or have insufficient space for the file being uploaded, and then randomly selects from the remaining valid paths, weighting the choice by their remaining capacity. If none of the valid paths that successfully reported free space have enough capacity for the file, **an error message is returned directly**. -::: tip -The load balancing mechanisms within the **Reading conflict policy** and the **Putting conflict policy** are two largely unrelated features. Informally speaking, the load balancing in the **Reading conflict policy** is analogous to RAID 1, while the load balancing in the **Putting conflict policy** is analogous to RAID 0. For specific use cases of each, please refer to [Advanced / Load balancing](/guide/advanced/balance). - -If you've understood the above, you'll realize that enabling both **Reading Load Balancing** and **Putting Load Balancing** won't make load balancing more balanced. In fact, this configuration produces effects that are hardly any different from enabling only **Putting Load Balancing**. +| 写入冲突策略 | file1.txt | file2.txt | file3.txt | +| -------------------------- | ------------------- | --------- | ------------ | +| 禁用 | 操作失败 | 操作失败 | 操作失败 | +| 写入首个有效路径 | 驱动A | 驱动B | 驱动B | +| 仅允许不冲突路径 | 操作失败 | 驱动B | 操作失败 | +| 仅允许全冲突路径 | 驱动A、驱动B、驱动C | 操作失败 | 操作失败 | +| 允许不冲突路径和全冲突路径 | 驱动A、驱动B、驱动C | 驱动B | 操作失败 | +| 写入所有有效路径 | 驱动A、驱动B、驱动C | 驱动B | 驱动B、驱动C | ::: -::: en -::: tip -The legacy version of the Alias used three Boolean configuration items—**Writable**, **Protect same name**, and **Parallel write**—to implement path conflict policy functionality. The correspondence between the legacy configuration and the new configuration is as follows: -- In the legacy configuration, the **Reading conflict policy** was always set to **Get the file corresponding to the first conflict path**. -- In the legacy configuration, when **Writable** was disabled, both the **Writing conflict policy** and the **Putting conflict policy** were set to **Disabled**. -- When **Writable** was enabled, the **Writing conflict policy** and the **Putting conflict policy** were both determined as follows: - | | Parallel write disabled | Parallel write enabled | - | -- | -- | -- | - | Protect same name enabled | Allow unique path | Allow unique path and full conflict paths | - | Protect same name disabled | Write/Put into the first conflict path | Write/Put into all conflict paths | +### 上传冲突策略 -::: -::: zh-CN 影响上传到、复制到、移动到、解压到同名文件夹的方式。 - 禁用、上传到首个有效路径、只允许不冲突路径、只允许全冲突路径、允许不冲突路径和全冲突路径、上传到所有有效路径:与**写入冲突策略**中的对应选项相同。 @@ -396,76 +180,37 @@ The legacy version of the Alias used three Boolean configuration items—**Writa 一旦理解了上述原理,你就会明白同时开启**读取负载均衡**和**上传负载均衡**并不能让你负载得均均又衡衡,这种配置产生的效果和仅开启**上传负载均衡**没有什么太大的区别。 ::: -::: zh-CN + ::: tip 旧版别名驱动使用**可写**、**保护同名**和**并行写入**三个布尔配置项实现路径冲突策略功能,旧版配置与新版配置的对应关系如下: - 在旧版配置中,**读取冲突策略**总为**读取首个有效路径**。 - 在旧版配置中,当**可写**关闭时,**写入冲突策略**与**上传冲突策略**均为**禁用**。 - 当**可写**开启时,**写入冲突策略**与**上传冲突策略**均为 - | | 关闭并行写入 | 开启并行写入 | - | -- | -- | -- | - | 开启保护同名 | 仅允许不冲突路径 | 允许不冲突路径和全冲突路径 | - | 关闭保护同名 | 写入/上传到首个有效路径 | 写入/上传到所有有效路径 | + | | 关闭并行写入 | 开启并行写入 | + | ------------ | ----------------------- | -------------------------- | + | 开启保护同名 | 仅允许不冲突路径 | 允许不冲突路径和全冲突路径 | + | 关闭保护同名 | 写入/上传到首个有效路径 | 写入/上传到所有有效路径 | ::: -## File consistency check { lang="en" } - -## 文件一致性校验 { lang="zh-CN" } - -::: en -When enabled, the driver will filter out paths where the **size or hash value** differs from other copies during the process of collecting valid paths. This is a safety measure, and whether it is enabled has relatively minor impact. It is recommended to enable this option when using the **Load balancing on a per-part basis** reading policy. - -Regardless of whether this option is enabled, the Alias does not actively calculate file hashes. Instead, it performs a best-effort match using the hash values returned by the backend drivers. - -Different types of hash values returned by backend drivers will not lead to misjudgment. For example, if Driver 1 returns the MD5 of a file and Driver 2 returns the SHA1 of the file, even with this option enabled, the Alias will not consider either path from Driver 1 or Driver 2 invalid simply because the file's MD5 and SHA1 are not equal. +## 文件一致性校验 -::: -::: zh-CN 开启时,驱动在收集有效路径的过程中,会过滤掉那些**大小或哈希值**与其它副本不同的路径,是一种安全措施,开启与否影响不太大,推荐使用**按分片负载均衡**读取策略时打开。 无论是否启用该选项,别名都不会主动计算文件的哈希值,而是使用后端驱动返回的哈希值做尽力而为的匹配。 后端驱动返回不同种类的哈希值不会导致误判。例如驱动 1 返回了文件的 MD5,驱动 2 返回了文件的 SHA1,即便开启本选项,别名也不会因为文件的 MD5 与 SHA1 不相等而判定驱动 1 或 2 中的任何一个路径无效。 -::: - -## The download method to use { lang="en" } - -## 使用的下载方式 { lang="zh-CN" } - -::: en -When adding **`alias`**, `Web Proxy` and `Webdav Policy` are not modified by default. The storage path filled in the Paths path can be `302`, `Local Proxy`, `Download Proxy URL`, three modes Mixed Playback Mixed Playback is possible. +## 使用的下载方式 -If you checked `Web Proxy`, the storage filled in by the Paths path, if there is a 302 mode playback, it will be played in transit (local proxy mode) at that time, and it will become a proxy mode. If the Webdav policy is also changed, it will also change. - -Of course, it is up to you to choose whether to change the mode. -::: -::: zh-CN 添加 **`别名-alias`** 时 `Web代理` 和 `Webdav策略` 默认不修改的情况下 Paths路径填写的存储路径可以 `302`,`本地代理`,`下载代理URL`,三种模式混合播放可以进行混合播放。 如果你勾选了`Web代理` ,Paths路径填写的存储 如果有302模式播放的 到时候都会进行中转(本地代理模式)播放,变成代理模式,Webdav策略如果也进行了改动也会进行变化。 当然,是否选择更换模式您自己进选择。 -::: - -### What if you don’t know how the cloud disks you added are different? { lang="en" } -### 如果你不知道你添加的云盘是什么方式怎么办? { lang="zh-CN" } - -::: en - -1. You can go to the bottom of the corresponding document to view the document, there is a flow chart description - - If there is a 302, the 302 method is used by default. If there are only local proxy and download proxy URLs, the default is to use the local proxy, provided that you have not manually selected -2. You can check when adding storage, select the corresponding storage to view, for example, let’s check the methods of Alibaba Cloud and 115 respectively - - As you can see from the figure below, Alibaba Cloud Disk has the option of `web proxy`, and `webdav policy` defaults to 302. It can be judged that Alibaba Cloud Disk uses the 302 method by default - - As you can see from the figure below, the 115 network disk does not have the option of `web proxy`, and the `webdav strategy` defaults to the local proxy. It can be judged that the 115 network disk uses the local proxy mode by default - ![alias](/img/drivers/alias/alias-2.png) - -::: - -::: zh-CN +### 如果你不知道你添加的云盘是什么方式怎么办? 1. 可以前往文档的相对应的文档最底部查看,有一份流程图说明 - 如果有302的就是默认使用302方式,如果只有本地代理和下载代理URL默认的就是使用本地代理,前提是你没有进行手动选择 @@ -474,75 +219,16 @@ Of course, it is up to you to choose whether to change the mode. - 从下图中看到,115网盘没有 `web代理`选择,`webdav策略`默认是本地代理,可以判断出115网盘默认使用的是本地代理方式 ![alias](/img/drivers/alias/alias-2.png) -::: - -## Proxy Range { lang="en" } +## 代理 Range -## 代理 Range { lang="zh-CN" } - -::: en -You need to enable `Web Proxy` or` Webdav Native Proxy` to take effect. Currently only applicable to:`alias`、`139Yun`、`OpenList V3`. - -- The `139Yun` driver, when this option is enabled, can resolve issues that occur when a proxy is enabled but the download link does not return the correct HTTP status code, such as problems with video playback or lack of support for resume downloads. -- The `Alias` driver is added to meet specific use cases, for example, when `139Yun` uses a 302 redirect. By enabling the `Alias` proxy, downloads can use `139Yun` with the 302 redirect, while video playback can use the proxy-enabled `Alias`, reducing unnecessary load. -- The `OpenList` driver is added to support server-side OpenList mounting with `139Yun` using a 302 redirect. Locally, OpenList can be mounted via the proxy-enabled `OpenList` to access the server's OpenList for video playback, etc., to avoid consuming server bandwidth. This also allows for data-free video streaming on mobile networks. - -::: - -::: zh-CN 需要先启用 `Web代理` 或者 `Webdav本地代理` 才会生效。目前仅适用于:`别名`、`中国移动云盘`、`OpenList V3`。 - `139Yun` 驱动开启这个选项后,可解决即使开启代理但下载链接没有返回正确的http状态码导致的一些问题,例如视频无法播放、不支持断点续传等 - `Alias` 驱动添加是为了满足某些场景,例如 `139Yun` 走302, 然后通过 开启代理的 `Alias` 后,下载用 `139Yun` 走302,播放视频用 开启代理的 `Alias`,减少不必要的负载 - `OpenList` 驱动添加是为了 服务器的 OpenList 挂载 `139Yun` 走302,然后本地的 OpenList 通过 开启代理的 `OpenList` 挂载 服务器的 OpenList 播放视频等,达到不消化服务器流量的目的,移动卡还能免流 -::: - -## Download concurrency, Download part size { lang="en" } - -## 下载并发、下载分片大小 { lang="zh-CN" } - -::: en -**Storage Settings:** - -- Alias (Alias) Drive -- Local Proxy -- Path: / Cloud Drive Mount Path -- Download Concurrency: 10 -- Download Chunk Size: 1024 - -**Effect:** - -- Client → OpenList Alias Drive: Uses 1 connection -- OpenList Alias Drive → Cloud Drive: Supports 10 concurrent connections, with actual concurrency limited by the cloud drive. - -```mermaid -graph LR - A[Client] -->|Connection 1| B[OpenList Alias Drive] - B -->|Concurrent Connection 1| C[Cloud Drive] - B -->|Concurrent Connection 2| C - B -->|Concurrent Connection 3| C - B -->|Concurrent Connection 4| C - B -->|Concurrent Connection 5| C - B -->|Concurrent Connection 6| C - B -->|Concurrent Connection 7| C - B -->|Concurrent Connection 8| C - B -->|Concurrent Connection 9| C - B -->|Concurrent Connection 10| C -``` - -- Single-threaded speed is slower, but it supports concurrency: Using the alias drive allows concurrent downloads, significantly improving speed. -- Video watching and download acceleration: The experience is enhanced by increasing concurrency. -- Copying from alias drive to other drives: File transfer is also accelerated in this case. +## 下载并发、下载分片大小 -**Friendly Reminder:** Please do not abuse this feature. Excessive use may cause abnormal activity on the cloud drive account, and you will bear the consequences. - -**Configuration Options:** - -- max_concurrency: Sets the maximum concurrency for the local proxy. The default is 64, and setting it to 0 means no limit on concurrency. - -::: -::: zh-CN **存储设置:** - 别名 (Alias) 驱动 @@ -581,27 +267,8 @@ graph LR - `max_concurrency`:设置本地代理的最大并发数,默认为 64,设置为 0 表示不限制并发数。 -::: - -## Other instructions { lang="en" } - -## 其他说明 { lang="zh-CN" } - -::: en - -If you are using `Windows`, the following situation will occur, and folders with different capitalization will also be regenerated. +## 其他说明 -For example, Local 1 and Local 2 have a lowercase v for `video` respectively, and the folder OneDrive has an uppercase V `Video` folder. - -Then a lowercase video folder will be generated, which contains only `local 1, local 2` files merged by two folders. - -At the same time, the uppercase `Video` will gather the files of the three folders. - -This is because Windows is case-insensitive, video and Video will be considered as the same folder, you will not have this problem if you switch to Linux or Mac. - -::: - -::: zh-CN 如果你是使用的`Windows`,会出现如下情况,有大小写不同的文件夹也会进行二次生成。 例如本地1,本地2 分别有 `video` 小写的v ,文件夹 OneDrive 有一个大写的 V `Video` 文件夹。 @@ -611,5 +278,3 @@ This is because Windows is case-insensitive, video and Video will be considered 同时大写的 `Video` 里面 会集合三个文件夹的文件 这是因为 Windows 大小写不敏感,video和Video会被认为是同一个文件夹,你换成 Linux 或者 Mac 就不会有这个问题了 - -::: diff --git a/pages/guide/drivers/alidoc.md b/pages/guide/drivers/alidoc.md index e7e10c989..e67bb28f6 100644 --- a/pages/guide/drivers/alidoc.md +++ b/pages/guide/drivers/alidoc.md @@ -1,34 +1,16 @@ --- -title: - en: AliDoc - zh-CN: 钉钉文档 top: 677 categories: - guide - drivers --- -::: en -::: danger Please read the notes carefully -This driver is currently not officially maintained by the project team. -::: +# 钉钉文档 -::: zh-CN ::: danger 请仔细阅读注意事项 本接口目前非项目组官方维护 ::: -::: en -Mount DingTalk Docs web storage in OpenList. - -Official website: - -- DingTalk Docs: - -This driver uses DingTalk Docs web APIs captured from the browser, not the official open platform API. -::: - -::: zh-CN 挂载钉钉文档网页端存储。 官方网站: @@ -36,18 +18,7 @@ This driver uses DingTalk Docs web APIs captured from the browser, not the offic - 钉钉文档: 本驱动使用的是从浏览器网页端抓取的钉钉文档 Web API,不是官方开放平台 API。 -::: - -::: en -::: warning Stability Notice - -Because this driver depends on web-side APIs and Cookie authentication, it may fail when DingTalk Docs changes its frontend behavior, request format, or login flow. - -Please use it with that risk in mind. - -::: -::: zh-CN ::: warning 稳定性提示 由于本驱动依赖网页端 API 和 Cookie 鉴权,钉钉文档一旦调整前端行为、请求格式或登录流程,就可能导致驱动失效。 @@ -56,25 +27,8 @@ Please use it with that risk in mind. ::: -## Supported operations { lang="en" } +## 支持的操作 -## 支持的操作 { lang="zh-CN" } - -::: en -Currently supported: - -- List files and folders -- Download files -- Upload files -- Create folders -- Move files and folders -- Copy files and folders -- Rename files and folders -- Recycle files and folders - -::: - -::: zh-CN 当前已支持: - 列出文件和文件夹 @@ -86,26 +40,8 @@ Currently supported: - 重命名文件和文件夹 - 删除到回收站 -::: - ## Cookie -::: en -Required. DingTalk Docs web Cookie. - -Recommended steps: - -1. Open a fresh browser session or incognito window. -2. Visit and log in to the account you want to mount. -3. Press `F12` to open developer tools. -4. Open the `Network` tab and refresh the page. -5. Search for requests such as `list`, `createfolder`, or other `/box/api/` requests. -6. Open any one of these requests and find the `Cookie` request header. -7. Copy the complete Cookie value into OpenList. - -::: - -::: zh-CN 必填。钉钉文档网页端 Cookie。 推荐按以下步骤获取: @@ -118,37 +54,12 @@ Recommended steps: 6. 打开任意一个请求,在请求头中找到 `Cookie`。 7. 复制完整 Cookie 值填入 OpenList。 -::: - -::: en -::: warning -Please avoid mixing multiple DingTalk accounts in the same browser environment when obtaining the Cookie. -::: - -::: zh-CN ::: warning 获取 Cookie 时请尽量避免同一浏览器环境里混用多个钉钉账号。 ::: -## Root folder ID { lang="en" } +## 根文件夹 ID -## 根文件夹 ID { lang="zh-CN" } - -::: en -Required. This is the UUID of the root folder entity used as the mount root. - -You can obtain it from DingTalk Docs web requests: - -1. Stay on the folder you want to mount as root. -2. Open developer tools and inspect a `/box/api/v2/dentry/list` request. -3. Find the `dentryUuid` request parameter. -4. Use that UUID as `Root folder ID`. - -Usually, the personal root folder UUID is also returned in responses such as `spaceProfile.rootDentryUuid`. - -::: - -::: zh-CN 必填。这里填写作为挂载根目录的文件夹实体 UUID。 可通过钉钉文档网页请求获取: @@ -160,24 +71,8 @@ Usually, the personal root folder UUID is also returned in responses such as `sp 有些响应里也会返回 `spaceProfile.rootDentryUuid`,个人空间根目录通常可以直接取这个值。 -::: - -## Notes { lang="en" } - -## 注意事项 { lang="zh-CN" } - -::: en - -- This driver depends on Cookie login state. If the Cookie expires, you need to refresh it manually. -- Upload uses DingTalk Docs web upload flow, including OSS upload and final commit request. -- Delete currently means moving the file or folder to the recycle bin, not permanent deletion. - -::: - -::: zh-CN +## 注意事项 - 本驱动依赖 Cookie 登录态,Cookie 失效后需要手动重新获取。 - 上传走的是钉钉文档网页端上传流程,包括 OSS 上传和最终提交请求。 - 当前“删除”表示移入回收站,不是永久删除。 - -::: diff --git a/pages/guide/drivers/aliyundrive_open.md b/pages/guide/drivers/aliyundrive_open.md index 097594da7..66ae4f148 100644 --- a/pages/guide/drivers/aliyundrive_open.md +++ b/pages/guide/drivers/aliyundrive_open.md @@ -1,51 +1,16 @@ --- -title: - en: Aliyundrive Open - zh-CN: 阿里云盘 Open -icon: iconfont icon-state -# This control sidebar order top: 699 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' - - '官方' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -::: tip -Aliyundrive Open, developed based on the official authorization API. -::: -::: zh-CN +# 阿里云盘 Open + ::: tip 阿里云盘 Open 基于官方授权 API 开发。 ::: -::: en -::: danger Read the precautions carefully - -1. The refresh token obtained by this tool can only be used for **Aliyundrive open platform** storage mount method. -2. The token filled in when OpenList is mounted should also be provided by this tool, and the previous acquisition method will not be available -3. Not applicable to others, but only for OpenList -4. If the same IP requests max times within minutes, **Too Many Requests** will appear. :no_entry_sign:So please do not abuse:no_entry_sign: - - viewing files and watching video downloads are not counted. The number of times and time mentioned above are dynamic~ - - New optimization: **will only refresh the token when the request finds that the token is expired, update restart editing if the token is still valid will not refresh.** -5. Online playback video reminder: **`ExceedCapacityForbidden`** error, the capacity exceeds restrictions, needs to be expanded or delete unnecessary files to save space,[View details](#four、) -6. **Do not share the Alibaba cloud disk publicly, and the account is prohibited from being accessed by multiple IPs. If the account is frozen after sharing, you will be responsible for the consequences. After adding storage, please set up meta information and other measures in time to prevent the account from being frozen by {other people/crawlers} malicious [access/sharing],Other cloud disks also need to be prevented from being frozen/banned.** -7. It is prohibited to use Alibaba Cloud Disk to build distribution services including but not limited to **`picture bed, software bed`** and **`video external link to video website playback`**, as well as some non-standard usage methods not mentioned above. If you violate the rules and are `frozen/banned`, you will be responsible for the consequences and it cannot be lifted. - -::: - -::: zh-CN ::: danger 请仔细阅读注意事项 1. 此工具获取的刷新令牌仅可以用于 **阿里云盘开放平台** 存储挂载方式 @@ -60,100 +25,25 @@ Aliyundrive Open, developed based on the official authorization API. ::: -## 1. Necessary Conditions { lang="en" } +## 1. 必要条件 -## 1. 必要条件 { lang="zh-CN" } - -::: en -A **AliyunDrive** account is required. -::: -::: zh-CN 必须有 **阿里云盘** 的帐号 -::: -::: en -::: warning Notes -Speed and stability are related to the performance/network of the machine running OpenList and the load on the AliYun Drive server. -::: -::: zh-CN ::: warning 注意事项 速度与稳定性与OpenList 的运行机器的性能/网络、阿里云盘服务器的负载有关 ::: -## 2. Preparation for Access { lang="en" } - -## 2. 准备接入 { lang="zh-CN" } - -### 2.1. Register an Application on the Open Platform (Optional; no need to create one if using the built-in key pair of OpenList/public welfare server/self-hosted server) { lang="en" } - -### 2.1. 开放平台注册应用(可选,如果使用OpenList/公益服务器/自建服务器内置的密钥对,则不用创建) { lang="zh-CN" } +## 2. 准备接入 -::: en -::: tip -Please register an application according to the requirements of the AliYun Drive Open Platform. -::: -::: en -Open Platform URL: -::: +### 2.1. 开放平台注册应用(可选,如果使用OpenList/公益服务器/自建服务器内置的密钥对,则不用创建) -::: zh-CN ::: tip 请根据阿里云盘开放平台的要求注册应用 ::: -::: zh-CN -开放平台地址: -::: - -### 2.2. Obtain Tokens { lang="en" } - -### 2.2. 获取令牌 { lang="zh-CN" } - -::: en - -1. Visit [api.oplist.org](https://api.oplist.org) **⚠️ If using a public welfare server/self-hosted server, visit the address of the public welfare server/self-hosted server.** - -2. Select **AliYun Drive App Login** from the drop-down menu. - - ![aliyun-open-01-d](/img/drivers/aliyun/aliyun-open-01-d.png#dark) - - ![aliyun-open-01-l](/img/drivers/aliyun/aliyun-open-01-l.png#light) - -3. If you are using the built-in key pair of `OpenList (or public welfare server/self-hosted server)` (i.e., you do not have application information from the AliYun Drive Open Platform), configure according to `3.1`, `3.2`, and `3.3`: - 3.1. Check `Use parameters provided by OpenList`. - - 3.2. Leave both `Client ID (ClientID/AppID)` and `App Secret (AppKey/Secret)` blank. - - 3.3. Click the `Get Token` button. - - ![aliyun-open-02-01-d](/img/drivers/aliyun/aliyun-open-02-01-d.png#dark) - - ![aliyun-open-02-01-l](/img/drivers/aliyun/aliyun-open-02-01-l.png#light) - -4. If you are using your own created OAuth client ID and secret, configure according to `4.1`, `4.2`, and `4.3`: - - 4.1. Do not check `Use parameters provided by OpenList`. - - 4.2. Enter your `AppId` in `Client ID` and your `AppSecret` in `App Secret`. - - 4.3. Click the `Get Token` button. - - ![aliyun-open-02-02-d](/img/drivers/aliyun/aliyun-open-02-02-d.png#dark) - - ![aliyun-open-02-02-l](/img/drivers/aliyun/aliyun-open-02-02-l.png#light) - -5. In the pop-up window, scan the code to log in to your AliYun Drive. - - ![aliyun-open-03](/img/drivers/aliyun/aliyun-open-03.png) - -6. After successful authorization, the page will display your `Access Token` and `Refresh Token`. Please copy and save these two tokens. - - ![aliyun-open-04](/img/drivers/aliyun/aliyun-open-04-d.png#dark) - - ![aliyun-open-04](/img/drivers/aliyun/aliyun-open-04-l.png#light) +开放平台地址: -::: -::: zh-CN +### 2.2. 获取令牌 1. 访问[api.oplist.org](https://api.oplist.org) **⚠️如果使用公益服务器/自建服务器,请访问公益服务器/自建服务器的地址** @@ -197,73 +87,9 @@ Open Platform URL: ![aliyun-open-04](/img/drivers/aliyun/aliyun-open-04-l.png#light) -::: - -## 3. Add AliYun Drive to OpenList { lang="en" } - -## 3. 在 OpenList 中添加阿里云盘 { lang="zh-CN" } +## 3. 在 OpenList 中添加阿里云盘 -### 3.1. Start Adding { lang="en" } - -### 3.1. 开始添加 { lang="zh-CN" } - -::: en - -1. Open the OpenList management interface and click `Storage` in the left menu. - -2. On the storage list page, click the `Add Storage` button in the upper right corner. - -3. Select the driver as `AliYun Drive (Oauth2)`. - -4. Enter the mount path, such as `aliyun-drive`. - -5. Enter the root folder ID obtained above in `Root Folder ID`. If using the root directory, enter `root`. See [Configuration Instructions/Root Folder ID](#_3-2-1-root-folder-id). - - ![aliyun-open-05-l](/img/drivers/aliyun/aliyun-open-05-l.png#light) - - ![aliyun-open-05-d](/img/drivers/aliyun/aliyun-open-05-d.png#dark) - -6. Enter the refresh token obtained above in the refresh token field (if not obtained, refer to [Preparation for Access](#_2-preparation-for-access)). - -7. If you are using the OAuth client ID and secret provided by `OpenList (or public welfare server/self-hosted server)`, configure according to `6.1`, `6.2`, and `6.3`: - - 6.1. Check `Use online api`. - - 6.2. Leave both `Client ID (ClientID/AppID)` and `App Secret (AppKey/Secret)` blank. - - 6.3. Enter `https://api.oplist.org/alicloud/renewapi` in `Api url address`. If using a `public welfare server/self-hosted server`, enter the corresponding server address. - - ![aliyun-open-06-l](/img/drivers/aliyun/aliyun-open-06-l.png#light) - - ![aliyun-open-06-d](/img/drivers/aliyun/aliyun-open-06-d.png#dark) - -8. If you are using your own created OAuth client ID and secret, configure according to `7.1`, `7.2`, and `7.3`: - - 7.1. Do not check `Use online api`. - - 7.2. Enter your `AppId` in `Client ID (ClientID/AppID)` and your `AppSecret` in `App Secret (AppKey/Secret)`. See [Configuration Instructions/Client ID, Secret](#_3-2-2-client-id-secret). - - 7.3. Leave `Api url address` blank. - - ![aliyun-open-07-l](/img/drivers/aliyun/aliyun-open-07-l.png#light) - - ![aliyun-open-07-d](/img/drivers/aliyun/aliyun-open-07-d.png#dark) - -9. In the `Instant Upload` option, select whether to enable the instant upload function (optional). See [Configuration Instructions/Instant Upload](#_3-2-3-instant-upload). - -10. In `Removal Method`, select the way to delete files (optional), default is recycle bin. See [Configuration Instructions/Removal Method](#_3-2-4-removal-method). - -11. In `Cloud Drive Type`, select the cloud drive type (optional), default is `Default`. See [Configuration Instructions/Cloud Drive Type](#_3-2-5-cloud-drive-type). - -12. In `Livp Download Format`, select the format for downloading live photos (optional), default is `Jpeg`. See [Configuration Instructions/Livp Download Format](#_3-2-6-livp-download-format). - -13. In `Internal Upload`, select whether to enable internal upload (optional), default is off. See [Configuration Instructions/Internal Upload](#_3-2-7-internal-upload). - -14. Click the `Add` button to complete adding AliYun Drive. - -::: - -::: zh-CN +### 3.1. 开始添加 1. 打开 OpenList 的管理界面,点击左侧菜单中的`存储`。 @@ -317,26 +143,10 @@ Open Platform URL: 14. 点击`添加`按钮,完成阿里云盘的添加。 -::: - -### 3.2. Configuration Instructions { lang="en" } - -### 3.2. 配置说明 { lang="zh-CN" } - -#### 3.2.1. Root Folder ID { lang="en" } - -#### 3.2.1. 根文件夹 ID { lang="zh-CN" } +### 3.2. 配置说明 -::: en -The default is `root`, displaying all cloud drive contents. If you only want to display contents within a certain folder, change it to `file_id`. +#### 3.2.1. 根文件夹 ID -Open the AliYun Drive official website, and when clicking into the folder to be set, click the string after the URL. - -For example, https://www.alipan.com/drive/folder/5fe01e1830601baf774e4827a9fb8fb2b5bf7940 - -The file_id of this folder is `5fe01e1830601baf774e4827a9fb8fb2b5bf7940`. -::: -::: zh-CN 默认为`root`,展示全部云盘内容,若只想展示某文件夹內内容,可以改为`file_id` 打开阿里云盘官网,点击进入要设置的文件夹时点击 URL 后面的字符串 @@ -344,52 +154,16 @@ The file_id of this folder is `5fe01e1830601baf774e4827a9fb8fb2b5bf7940`. 如 https://www.alipan.com/drive/folder/5fe01e1830601baf774e4827a9fb8fb2b5bf7940 这个文件夹的 file_id 即为 `5fe01e1830601baf774e4827a9fb8fb2b5bf7940` -::: ![file_id](/img/drivers/aliyundrive.png) -#### 3.2.2. Client ID, Secret { lang="en" } - -#### 3.2.2. 客户端 ID,密钥 { lang="zh-CN" } - -::: en -Normal users can leave it blank. If you have applied for official authorization, you can fill in your own. When blank, it defaults to using those provided by OpenList. - -- If using your own developer account, the Oauth token link must also be modified, and you must use your own developer account ID and secret to obtain the refresh token. +#### 3.2.2. 客户端 ID,密钥 -::: - -::: zh-CN 正常用户不需要填写为空即可,如果自己申请了官方授权也可以使用自己的进行填写,为空时默认使用 OpenList 提供的。 - 如果使用自己的开发者帐号,Oauth令牌链接也要修改,同时也要使用自己开发者帐号ID和密钥获取刷新令牌 -::: - -#### 3.2.3. Instant Upload { lang="en" } - -#### 3.2.3. 秒传 { lang="zh-CN" } - -::: en - -- **Version v.3.22.1** new feature. - - Instant upload requires enabling `Instant Upload` when mounting. The prerequisite for instant upload is that the file already exists in AliYun Drive; otherwise, it will be a normal upload. - - Instant upload calculates the file's `sha1` value (should be correct) without requiring you to calculate it. You just need to upload, and it will automatically calculate after upload completion. - -- It uses the interface provided by AliYun Drive's official API. For details, see :point_right: [**Instant Upload Logic**](https://www.yuque.com/aliyundrive/zpfszx/ezlzok#y7lyH). - ---- - -- **Version v.3.27.0** enhances instant upload: files can be instantly transferred between `115 Network Disk` and AliYun Drive by direct copying. - - Prerequisite: To instantly transfer a file from AliYun Drive to 115, the file must already exist in 115; otherwise, it will be a normal copy task. - - If instantly transferring a file from 115 to AliYun Drive, enable the instant upload option for AliYun Drive; otherwise, it will be a normal upload. - It is recommended to locally deploy an OpenList on your home computer, add a `local storage` and `AliYun Drive`, and use copy instant upload to save resources. - -::: - -::: zh-CN +#### 3.2.3. 秒传 - **v.3.22.1** 版本 新增功能 @@ -407,24 +181,8 @@ Normal users can leave it blank. If you have applied for official authorization, 如果要使用秒传来上传文件建议在自己家用电脑本地搭建一个OpenList添加一个`本地存储`和`阿里云盘`进行复制秒传这样节省资源。 -::: - -#### 3.2.4. Removal Method { lang="en" } - -#### 3.2.4. 移除方式 { lang="zh-CN" } - -::: en -If not set, it defaults to deleting to the recycle bin. If space is insufficient, you need to manually clean the recycle bin. If a file is deleted by mistake, please go to the AliYun Drive official website client to retrieve it. +#### 3.2.4. 移除方式 -- **Recycle Bin**: Deleting in OpenList will move the file to the network disk recycle bin, occupying cloud drive space, but it can be retrieved later if deleted by mistake. -- **Delete**: Direct deletion without staying in the recycle bin, not occupying cloud drive space, but cannot be retrieved later if deleted by mistake. - - Note: Do not leak the Token obtained from AliYun Drive Open. If leaked by mistake, please immediately: - - **Open AliYun Drive app → My → Settings gear icon in the upper right corner → Privacy Settings → Authorization Management → Click OpenList to view and revoke authorization**. - - Then rescan the code to authorize for a new refresh token. Revoking authorization will invalidate all previously obtained tokens to protect your account security. - -::: - -::: zh-CN 不进行设置默认是删除到回收站內了,如果空间不够需要自己手动清理回收站/若删错文件请前往阿里云盘官网客户端进行找回恢复 - **回收站**:在OpenList删除后进入网盘回收站,会占用云盘空间,但是后期如果误删可以找回。 @@ -433,23 +191,8 @@ If not set, it defaults to deleting to the recycle bin. If space is insufficient - **打开-阿里云盘-app-我的-右上角设置齿轮-隐私设置-授权管理-点击-openlist-进行查看和解除授权** - 再重新扫码授权换新的刷新令牌,同时解除授权后之前获取的都会失效,以保护您账号的安全 -::: - -#### 3.2.5. Cloud Drive Type { lang="en" } - -#### 3.2.5. 云盘类型 { lang="zh-CN" } - -::: en -You can select the cloud drive type according to your needs. When adding a driver using OpenList version v3.25.1 and above, the default selection is `Default`. +#### 3.2.5. 云盘类型 -- **Default**: For users with a talent level, it defaults to displaying `non-backup file` content; for users without a talent level, it defaults to displaying `backup file` content. -- **Resource Library**: Corresponding to the `non-backup file content` of the cloud drive. -- **Backup Disk**: Corresponding to the `backup files` of the cloud drive. - After AliYun Drive updated to version `6.0` in July 2024, the backup disk was changed to merge backup files and all files, but in terms of OpenAPI, it still distinguishes between `Resource Library` and `Backup Disk` types. - ![](/img/drivers/aliyun/aliyun_type.png) - -::: -::: zh-CN 云盘类型可以选择,可以自己的需求选择不同类型,使用 v3.25.1及以上版本的OpenList添加驱动时进行设置,默认选择的是`默认`类型 - **默认**:有达人等级用户默认展示的是`非备份文件`内容,没有达人等级用户默认展示的是`备份文件`内容 @@ -458,67 +201,22 @@ You can select the cloud drive type according to your needs. When adding a drive 202407 --> 阿里云盘更新 `6.0` 版本后,备份盘变更为备份文件和全部文件合并在一起了,但是在OpenAPI方面还是区分 `资源库` 和 `备份盘` 类型 ![](/img/drivers/aliyun/aliyun_type.png) -::: - -#### 3.2.6. Livp Download Format { lang="en" } - -#### 3.2.6. Livp 下载格式 { lang="zh-CN" } - -::: en -That is, iOS `live` photos cannot be parsed by non-iOS systems, so they cannot be downloaded. Now, you can choose `Jpeg` or `Mov` according to your needs. - -- Jpeg: The cover of the `live` photo. -- Mov: The video frame of the `live` photo when long-pressed. +#### 3.2.6. Livp 下载格式 -::: - -::: zh-CN 即 IOS`实况`照片因为非IOS系统无法解析,然后就无法下载,现根据自己的需求是需要`Jpeg`还是`Mov` - Jpeg:就是`实况`照片所展示的封面 - Mov:长按`实况`照片的视频画面 -::: - -#### 3.2.7. Internal Upload { lang="en" } - -#### 3.2.7. 内部上传 { lang="zh-CN" } - -::: en -If the server where OpenList is deployed is an AliYun ECS in the Beijing region, turning on this switch can improve file upload speed. Do not turn on this switch for servers that do not meet the requirements, otherwise, upload failures will occur. - -- Can `Internal Upload` be used on non-Beijing AliYun ECS? No, because AliYun Drive uses object storage in the Beijing region. +#### 3.2.7. 内部上传 -::: - -::: zh-CN 如果你部署 OpenList 的服务器是阿里云北京地区ECS,打开此开关可以提升文件上传速度。不符合要求的服务器请不要打开此开关,否则会出现无法上传的问题。 - **内部上传**非北京地区的阿里云ECS可以使用吗?不能,因为阿里云盘在使用北京地区的对象存储 -::: - -## 4. Other Instructions { lang="en" } - -## 4. 其他说明 { lang="zh-CN" } - -### 4.1. Permission Scope { lang="en" } - -### 4.1. 权限范围 { lang="zh-CN" } +## 4. 其他说明 -::: en - -OpenList only obtains cloud drive user (**name, avatar ¹**), (**file access permission ²**), and (**file write permission ³**), without obtaining phone number permission. - -Details can be viewed in the following two ways: - -1. The **prompt** when scanning the code to obtain the refresh token. -2. Open **AliYun Drive app → My → Settings gear icon in the upper right corner → Privacy Settings → Authorization Management → Click OpenList** to view. - - You can manually revoke permissions at any time if not in use. - -::: - -::: zh-CN +### 4.1. 权限范围 OpenList 仅获取了 云盘用户 (**名称 头像 ¹**),(**访问文件权限 ²**)和(**写入文件权限 ³**),未获取手机号权限 @@ -529,25 +227,8 @@ OpenList 仅获取了 云盘用户 (**名称 头像 ¹**),(**访问文件权 2. 打开 **阿里云盘 APP --> 我的 -->右上角设置齿轮 --> 隐私设置 --> 授权管理 --> 点击 OpenList** 进行查看 - 若不使用了可以随时手动解除权限 -::: +### 4.2. 关于视频播放 -### 4.2. About Video Playback { lang="en" } - -### 4.2. 关于视频播放 { lang="zh-CN" } - -::: en - -The default player is AliYun Drive's built-in player - **Aliyun Video Previewer**. -Videos uploaded to the cloud drive will be transcoded into H.264-encoded video streams through the transcoding service. (The benefit is that iOS can play them... Well, it should be like this.) - -- For newly uploaded videos, only the first 30s of the video is pre-transcoded by default. The first play will trigger full video transcoding. The transcoded video will be cached, and no retranscoding is needed for subsequent plays. -- Videos shorter than 30s will not trigger pre-transcoding, and transcoding will only start when played for the first time. -- Real-time transcoding takes some time. -- Video qualities are: LD|SD|HD|FHD|QHD. - -::: - -::: zh-CN 默认使用的是阿里云盘自带播放器 - **Aliyun Video Previewer** 上传到云盘的视频,会经过转码服务转成 H.264 编码的视频流。(好处是 IOS 可以播放了...嗯 应该是这样) @@ -557,43 +238,13 @@ Videos uploaded to the cloud drive will be transcoded into H.264-encoded video s - 实时转码需要一定时间。 - 画质分别为:LD|SD|HD|FHD|QHD -::: - -### 4.3. Inability to View Office Suite Files { lang="en" } - -### 4.3. Office 全家桶文件无法查看 { lang="zh-CN" } - -::: en - -> Q: Why can't Office suite files be viewed in AliYun Drive Open? -> -> A: ~~Because AliYun Drive has not opened the relevant API, so it cannot be viewed temporarily~~ (Stay tuned.) - -::: - -::: zh-CN +### 4.3. Office 全家桶文件无法查看 > Q:阿里云盘Open怎么看不了 Office 全家桶类型的文件 > > A:~~因为阿里云盘未开放相关API故暂时无法查看~~(敬请期待) -::: - -### 4.4. Subtitle Loading { lang="en" } - -### 4.4. 字幕加载 { lang="zh-CN" } - -::: en - -Q: How to load subtitles? - -A: AliYun Drive Open needs to use the **Aliyun Video Previewer** player, and currently only supports transcoding srt and vtt text format subtitles encapsulated in mkv. - -- Currently, only three languages are supported: eng, jpn, chi. Other languages will be lost. - -::: - -::: zh-CN +### 4.4. 字幕加载 Q:如何加载字幕? @@ -601,31 +252,7 @@ A:阿里云盘open 需要使用 **Aliyun Video Previewer**播放器,然后 - 目前只支持转码 eng、jpn、chi 三种语言,其他语言会丢失 -::: - -### 4.5. Common Issues { lang="en" } - -### 4.5. 常见问题 { lang="zh-CN" } - -::: en - -1. ExceedCapacityForbidden - - Q: When playing AliYun Drive videos online, it cannot play and prompts the error: **`ExceedCapacityForbidden`**. - - A: **The user's capacity is exceeded**, restricting playback. You need to expand the capacity or delete unnecessary files to free up space - - In OpenList, it prompts: `ExceedCapacity Forbidden: Execeed Capactity Forbidden`. - - In AliYun Drive, it prompts: `云盘可用空间不足` (Insufficient available space in cloud drive). - -2. Transcoding in progress, please try again later. - - Q: When viewing a video in AliYun Drive, it prompts `Transcoding in progress, please try again later`, and when mounting in OpenList, it prompts: `failed link: failed get link: UserNotAllowedAccessResource: user not allowed access resource for violation`. - - A: It means the account has been frozen (previously, it was directly unable to log in). For specific situations, please check the red content notes at the top. - -::: - -::: zh-CN +### 4.5. 常见问题 1. ExceedCapacityForbidden @@ -641,35 +268,7 @@ A:阿里云盘open 需要使用 **Aliyun Video Previewer**播放器,然后 A:代表帐号已经被冻结(以前都是直接无法登录),具体情况请查看顶部的红色内容注意事项 -::: - -## 5. The default download method used { lang="en" } - -## 5. 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 5. 默认使用的下载方式 ```mermaid --- @@ -689,5 +288,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/autoindex.md b/pages/guide/drivers/autoindex.md index a27866879..1b077a32e 100644 --- a/pages/guide/drivers/autoindex.md +++ b/pages/guide/drivers/autoindex.md @@ -1,46 +1,17 @@ --- -title: - en: AutoIndex - zh-CN: AutoIndex -icon: iconfont icon-state top: 350 categories: - guide - drivers --- -::: en - -The AutoIndex driver is used to mount directory index pages built into HTTP servers, such as the [Nginx Source Index](https://nginx.org/download/) and [Redis Releases](https://download.redis.io/releases/). - -This driver essentially scrapes page information directly and requires manually specifying XPaths, making it suitable for scraping pages that are not directory index pages as well. - -::: - -::: zh-CN +# AutoIndex AutoIndex 驱动用于挂载 HTTP 服务器内置的目录索引页,如[Nginx Source Index](https://nginx.org/download/)、[Redis Releases](https://download.redis.io/releases/)。 该驱动本质上是直接爬取页面信息,需要手动填写 XPath,因此也可以用来爬取那些不是目录索引页的页面。 -::: - -## Setup Instructions { lang="en" } - -## 配置说明 { lang="zh-CN" } - -::: en - -- **URL:** The website address. If no scheme is included, `https://` will be automatically added. To mount a subpath, append it directly to the end of the URL. For example, can be used to mount the subpath `tomcat/` under . Required. -- **Entry XPath:** An XPath expression used to match each entry node in the file list. The result of this expression must be a **node-set**, where each node corresponds to a file entry. Required. -- **Filename XPath:** Within the context of each node matched by **Entry XPath**, this XPath expression is used to extract the filename. The result can be a node, node-set, or string. If the result is a node or node-set, the innerText of the first node will be used as the filename. Required. -- **Modification time XPath:** Within the context of each node matched by **Entry XPath**, this XPath expression is used to extract the file modification time. The result can be a node, node-set, or string. If the result is a node or node-set, the innerText of the first node will be used as the modification time string. Optional. If left blank, no node is matched, or the date format is unrecognizable, the current time will be used by default. -- **File size XPath:** Within the context of each node matched by **Entry XPath**, this XPath expression is used to extract the file size. The result can be a node, node-set, number, or string. If the result is a node or node-set, the innerText of the first node will be parsed as the file size. Optional. If left blank, no node is matched, or the content cannot be recognized as a valid size, the driver will return a file size of 0. -- **Ignore filenames:** Used to filter out entries that should not be crawled (e.g., list headers, parent directory links). Enter the filenames to ignore (without the trailing `/`). -- **Modification date format:** Enter a time string matching the format displayed on the page (refer to the Go time template: `Mon Jan 2 15:04:05 -0700 MST 2006`, see also: [Go Time Formatting Documentation](https://golang.org/pkg/time/#pkg-constants)). - -::: -::: zh-CN +## 配置说明 - **URL:** 网站地址,如不含 scheme 会自动添加`https://`,需要挂载子路径可直接在链接结尾添加,如可用于挂载的子路径`tomcat/`,必填。 - **条目 XPath:** 用于匹配文件列表中每个条目节点的 XPath 表达式,该表达式的执行结果必须为**节点集**,其中每个节点对应一个文件条目。此项为必填。 @@ -50,33 +21,11 @@ AutoIndex 驱动用于挂载 HTTP 服务器内置的目录索引页,如[Nginx - **忽略文件名:** 用于过滤不需要抓取的条目(例如列表表头、返回上级目录的链接等)。填写希望忽略的文件名(无需包含结尾的`/`)。 - **修改日期格式:** 填写与页面显示格式对应的时间字符串(参考 Go 时间模板:`Mon Jan 2 15:04:05 -0700 MST 2006`,另请参阅: [Go时间格式化文档](https://golang.org/pkg/time/#pkg-constants)) -::: +## 参考配置 -## Reference Configuration { lang="en" } - -## 参考配置 { lang="zh-CN" } - -::: en -The following configurations can be used to mount the auto-index pages of some HTTP servers. Since the style of auto-index pages may change with updates to the HTTP server version, the configurations provided below are for reference only and are not guaranteed to work across all versions. You are welcome to supplement available configurations for other HTTP servers in the comments. -::: -::: zh-CN 以下为挂载部分 HTTP 服务器 auto-index 页的可用配置。由于 auto-index 页的样式可能随 HTTP 服务器的版本更新而发生改变,以下配置仅供参考,不保证在所有版本中都可以使用。欢迎在评论中补充其它 HTTP 服务器的可用配置。 -::: - -#### Nginx (Tested on 1.29.0) { lang="en" } -#### Nginx(测试版本 1.29.0) { lang="zh-CN" } - -::: en - -- Entry XPath: `//pre/a` -- Filename XPath: `.` -- Modification time XPath: `substring(normalize-space(./following-sibling::text()[1]),1,17)` -- File size XPath: `substring(normalize-space(./following-sibling::text()[1]),19)` -- Modification date format: `02-Jan-2006 15:04` - -::: -::: zh-CN +#### Nginx(测试版本 1.29.0) - 条目 XPath:`//pre/a` - 文件名 XPath:`.` @@ -84,22 +33,7 @@ The following configurations can be used to mount the auto-index pages of some H - 文件大小 XPath:`substring(normalize-space(./following-sibling::text()[1]),19)` - 修改时间格式:`02-Jan-2006 15:04` -::: - -#### Apache httpd (Tested on 2.4.18) { lang="en" } - -#### Apache httpd(测试版本 2.4.18) { lang="zh-CN" } - -::: en - -- Entry XPath: `//table/tbody/tr[position() > 2]` -- Filename XPath: `./td[2]/a` -- Modification time XPath: `./td[3]` -- File size XPath: `./td[4]` -- Modification date format: `2006-01-02 15:04` - -::: -::: zh-CN +#### Apache httpd(测试版本 2.4.18) - 条目 XPath:`//table/tbody/tr[position() > 2]` - 文件名 XPath:`./td[2]/a` @@ -107,22 +41,7 @@ The following configurations can be used to mount the auto-index pages of some H - 文件大小 XPath:`./td[4]` - 修改时间格式:`2006-01-02 15:04` -::: - -#### Caddy (Tested on v2.10.2) { lang="en" } - -#### Caddy(测试版本 v2.10.2) { lang="zh-CN" } - -::: en - -- Entry XPath: `//table/tbody/tr` -- Filename XPath: `./td[2]/a/span` -- Modification time XPath: `./td[4]/time` -- File size XPath: `./td[3]/div/div[2]` -- Modification date format: `01/02/2006 03:04:05 PM -07:00` - -::: -::: zh-CN +#### Caddy(测试版本 v2.10.2) - 条目 XPath:`//table/tbody/tr` - 文件名 XPath:`./td[2]/a/span` @@ -130,22 +49,7 @@ The following configurations can be used to mount the auto-index pages of some H - 文件大小 XPath:`./td[3]/div/div[2]` - 修改时间格式:`01/02/2006 03:04:05 PM -07:00` -::: - -#### Python SimpleHTTP (Tested on 3.11.5-0.6) { lang="en" } - -#### Python SimpleHTTP(测试版本 3.11.5-0.6) { lang="zh-CN" } - -::: en - -- Entry XPath: `//ul/li` -- Filename XPath: `./a` -- Modification time XPath: leave blank -- File size XPath: leave blank -- Modification date format: leave blank - -::: -::: zh-CN +#### Python SimpleHTTP(测试版本 3.11.5-0.6) - 条目 XPath:`//ul/li` - 文件名 XPath:`./a` @@ -153,35 +57,7 @@ The following configurations can be used to mount the auto-index pages of some H - 文件大小 XPath:留空 - 修改时间格式:留空 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -201,5 +77,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/baidu.md b/pages/guide/drivers/baidu.md index 3adf31c0d..a27ef363d 100644 --- a/pages/guide/drivers/baidu.md +++ b/pages/guide/drivers/baidu.md @@ -1,38 +1,12 @@ --- -title: - en: Baidu Netdisk - zh-CN: 百度网盘 -icon: iconfont icon-state -# This control sidebar order top: 690 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' - - '官方' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -::: tip -Due to the limitation of Baidu Disk API, downloading files larger than about 20M needs to carry the header: "User-Agent": "pan.baidu.com", so when downloading files larger than 20M, you need to set the request header yourself, such as using curl: - -```bash -curl -L -X GET 'YOUR_LINK' -H 'User-Agent: pan.baidu.com' -``` +# 百度网盘 -Or use the proxy function in this program to transfer. -::: - -::: zh-CN ::: tip 由于百度网盘 API 的限制,下载大于 20M 左右的文件需要携带 header:"User-Agent":"pan.baidu.com",所以下载大于 20M 的文件时,需要设置请求头,例如使用 curl: @@ -43,44 +17,6 @@ curl -L -X GET 'YOUR_LINK' -H 'User-Agent:pan.baidu.com' 或者使用本程序中的代理功能进行传输。 ::: -::: en - -```mermaid ---- -title: How to Stream from Baidu Cloud? ---- -flowchart TB - a[(Baidu Cloud)] - 1[Official API] - b[Super VIP] - c[Modify UA] - d[Can Modify UA] - e[Cannot Modify UA] - f[Local Proxy] - g[High Bandwidth] - k[Low Bandwidth, Cannot Handle] - l[Can Stream] - m[End] - - a ==> 1 - 1 ==> b - b ==> c - b -.-> m - c ==> d - c -.-> e - d ==> l - e ==> f - f ==> g - f -.-> k - g ==> l - k ==> m - click c,e,d,m "#Add-User-Agent-Example" -``` - -::: - -::: zh-CN - ```mermaid --- title: 百度云盘如何在线播放? @@ -113,89 +49,8 @@ flowchart TB click c,e,d,m "#添加-user-agent-使用示例" ``` -::: - -## Refresh token { lang="en" } - -## 刷新令牌 { lang="zh-CN" } - -::: en -**Currently, there are three methods to obtain a Baidu Netdisk refresh token:** - -All methods require you to [click here](https://api.oplist.org/) to obtain the refresh token. The **first method** requires developer permissions, while the **latter two** do not. - -1. If you have Baidu Netdisk developer permissions, please select **"Baidu Netdisk Auth Login"**, and in the Baidu Netdisk developer app backend, set the callback URL to the one provided on the webpage. After clicking "Get Token," you will automatically receive a refresh token. You can then configure your own `client_id` and `client_secret` in OpenList. - - (Please note: If using Baidu Netdisk's API, ensure the “Use Online API” option is unchecked, otherwise it will prevent connection.) - - ![](/img/drivers/baidu/dev_token.png) - - Want to obtain developer permissions? Please follow the steps below to apply for developer permissions on Baidu Cloud Open Platform: - - (Please note that we do not advocate the abuse of Baidu Netdisk developer permissions. You need to bear the operational risks yourself.) - 1. Please go to [Baidu Open Platform](https://pan.baidu.com/union). - - ![](/img/drivers/baidu/dev_platform.png) - - 2. Log in to your Baidu account. - - ![](/img/drivers/baidu/dev_platform_login.png){width=200px} - - 3. Follow the steps to complete the developer certification (supplement account information, such as real-name authentication, email, etc.). - - 4. After completion, the system will automatically redirect to the [Baidu Open Platform Console](https://pan.baidu.com/union/console/applist). - - If you cannot access the console, you may not have completed the certification. Please return to step one and try again. - - ![](/img/drivers/baidu/dev_platform_console.png){width=600px} - - 5. Create an application using the create button in the console. - - (Please note that individual developers can only create one application. If you have other uses for the application, please use methods two and three. Here we assume you don't have an application and are willing to create one for OpenList.) - - ![](/img/drivers/baidu/dev_app_create_button.png){width=600px} - - 6. Select "Software" as the application category, and fill in the application name and description according to your preference. Please note that these three contents cannot be changed once set, so please think carefully before filling them out. - - ![](/img/drivers/baidu/dev_app_create_panel.png){width=600px} - - 7. After creation is complete, you'll return to the control panel. Click on the application name to enter the application details page. - - (Please note that you don't need to apply for online review to use it normally.) - - ![](/img/drivers/baidu/dev_app_console.png){width=200px} - - 8. Select Security Settings and fill in the application callback URL with the provided callback address and save. - - ![](/img/drivers/baidu/dev_app_info.png){width=600px} - - ``` - https://api.oplist.org/baiduyun/callback - ``` - - ![](/img/drivers/baidu/dev_app_settings_safety.png){width=600px} - - 9. Return to the application details page and copy the necessary AppKey and SecretKey. - - 10. Use the [OpenList Token Acquisition Tool](https://api.oplist.org/) to log in via Baidu Netdisk authentication and fill in the corresponding keys to obtain the token. - - 11. Fill in the corresponding parameters and settings according to the image at the beginning of this method to start using it. - -2. This method uses a built-in API call via our intermediary server to refresh the access token. It is already available online. Simply check the option to use the parameters provided by OpenList, click "Get Token," and you will receive a refresh token. In the storage configuration page of OpenList, enable the "Use Online API" option, and enter the refresh token to start using it. - - ![](/img/drivers/baidu/non_dev_token.png) - - The basic principle of the online API is illustrated in the diagram below: - - ![](/img/drivers/baidu/openapi.png) - -3. If you do not have Baidu Netdisk developer permissions but somehow have access to an app’s `client_id` and `client_secret`, please select **"Baidu Netdisk OOB Authentication"**. By default, this method uses the client parameters from the "ES File Explorer" app, but you can also input your own `client_id` and `client_secret`. After clicking "Get Token," wait patiently as you are redirected to the Baidu authorization page. Log in and authorize the app, then copy the authorization code and return to the original webpage to input the code as instructed to obtain the refresh token. In the storage configuration page of OpenList, fill in the `client_id` and `client_secret` to start using the service. +## 刷新令牌 - ![](/img/drivers/baidu/crack_dev_token.png) - -::: - -::: zh-CN **目前你有三种方法获取百度网盘的刷新令牌:** 全部都要[点击这里](https://api.oplist.org/) 来获取刷新令牌。第一种需要开发者权限,后**两种**不需要。 @@ -269,74 +124,18 @@ All methods require you to [click here](https://api.oplist.org/) to obtain the r ![](/img/drivers/baidu/crack_dev_token.png) -::: - -## Root folder path { lang="en" } - -## 根文件夹ID { lang="zh-CN" } +## 根文件夹ID -::: en -The root foler to mount, defaults to `/` - -- Mount a folder separately, according to the following format, `/` is the root directory, just extend to that directory if you want to mount that directory - - /folder_A/....../folder_x - -::: - -::: zh-CN 要挂载的根文件夹,默认为`/` - 单独挂载某文件夹,按照下面格式,`/`是根目录,想挂载那个目录就延伸到那个目录就可以 - /文件夹-A/……/文件夹-x -::: - -## ~~**Custom crack ua**~~ { lang="en" } +## ~~**自定义破解ua**~~ -## ~~**自定义破解ua**~~ { lang="zh-CN" } - -::: en -~~[**UA used when using 【Native Proxy & Crack API】**](https://github.com/alist-org/alist/issues/5602#issuecomment-1831188682)~~ The unofficial interface is no longer available - -::: -::: zh-CN ~~[**使用【本地代理 & Crack API】时候使用的UA**](https://github.com/alist-org/alist/issues/5602#issuecomment-1831188682)~~ 非官方接口已无法使用 -::: - -## Download api { lang="en" } - -## 下载接口 { lang="zh-CN" } - -::: en - -- **Official:** The official interface, very stable, but for larger files, UA needs to be modified and the speed is slow (SVIP speed fast) -- **Crack:** unofficial interface,**Seems to be no longer available** ~~You must modify the UA and some files may not have a speed limit, but it will be unstable (not guaranteed 100%availability) Need to use a version greater than `3.19.0`~~ - - ~~Need to change the UA to`netdisk`,Modification method refer to [add-user-agent-user-example](#add-user-agent-usage-example)~~ - - ~~Or turn on the Web proxy, you do not need to modify the UA (you need a large broadband to use it stably)~~ - - ~~It is limited to play/download **`video (only tested in the mp4 format other formats are not tested)`**,If it is other format files, such a prompt will appear~~ - - ~~This is not a mistake, this is a limit, please do not panic.~~ -- **Crack video:** unofficial video interface, dedicated to playing videos, the following errors will occur in non-video formats, which is normal - - The browser also needs to modify the UA: `pan.baidu.com` or `netdisk` In short, if it is not the browser User-Agent, the video can be played. - - The specific situation is the same as the previous unofficial interface usage - - The continuous use time is unknown, and 100% availability is not guaranteed - - ```json{2-4,7-9} - { - error_code: 31119, - error_msg: "hit black userlist , hit illeage dlna", - request_id: 541111111111111140 - }, - { - error_code: 31329, - error_msg: "hit black userlist , hit illeage dlna", - request_id: 921111381111111100 - } - ``` - -::: - -::: zh-CN +## 下载接口 - **Official**:官方接口,很稳定,但是文件比较大,需要修改UA,速度慢 (SVIP速度快) - **Crack**:非官方接口,**似乎已经无法使用了**~~现在也需要修改UA且部分文件可能不限速,但是会不稳定(不保证100%可用性)需要使用大于`3.19.0`的版本~~ @@ -363,24 +162,8 @@ The root foler to mount, defaults to `/` } ``` -::: - -## Add "User-Agent" usage example { lang="en" } - -### 添加 "User-Agent" 使用示例 { lang="zh-CN" } - -::: en -::: danger If you don't know how to set "User-Agent" see here -The following methods are limited to Baidu super member users +### 添加 "User-Agent" 使用示例 -Prompt again The following methods are limited to Baidu super member users - -It will only be useful if a member finishes modifying **`"User-Agent"`** (choose official and 302) - -If you don’t change **`"User-Agent"`**, you can enable=Web Agent, the disadvantage is that you need to build an OpenList machine for transfer, that is to say, you need a large broadband to help you transfer -::: - -::: zh-CN ::: danger 如果你不会设置 "User-Agent" 请看这里 以下方法仅限于有百度超级会员用户使用 @@ -391,32 +174,6 @@ If you don’t change **`"User-Agent"`**, you can enable=Web Agent, the disadvan 如果不改 **`"User-Agent"`**,可以开启 Web代理,缺点是需要你搭建OpenList的机器中转,也就是说你需要大宽带帮你中转 ::: -::: en - -
-

- Tutorial on how to modify UA in web page 302 mode -

-
-
-
Official
- -
-
-
Crack video (unavailable)
- -
-
-
- -::: - -::: zh-CN -

网页302模式修改UA教程 @@ -437,23 +194,7 @@ If you don’t change **`"User-Agent"`**, you can enable=Web Agent, the disadvan

-::: - -### Web plugin { lang="en" } - -### 网页插件 { lang="zh-CN" } - -::: en - -The advantage of using a browser plug-in to modify is that it can be played directly online, of course, downloading is also possible. - -Example 1: If you really don’t know it, you can take a look at an example on the web page: **https://www.bilibili.com/video/BV1UA4y1X7J8** - -Example 2: Another plugin method covering 360, Chrome, Edge: **https://youtu.be/PP6b0WSzYMc** - -::: - -::: zh-CN +### 网页插件 使用浏览器插件修改的好处是 可以直接在线播放,当然了下载也是可以的。 @@ -461,29 +202,9 @@ Example 2: Another plugin method covering 360, Chrome, Edge: **https://youtu.be/ 例2:另一款插件方法涵盖360,Chrome,Edge: **https://youtu.be/PP6b0WSzYMc** -::: - ![bdUA](/img/drivers/baidu/bdUA.png) -### Aria2 { lang="en" } - -### Aria2 { lang="zh-CN" } - -::: en - -1. First follow the figure below to set the **`user-agent" `**, and then click the button option in the lower right corner, click ** gear ** (local settings) to configure the parameters well - - ![aria2-ua](/img/drivers/baidu/aria2-ua.png) - -2. Then open the third button option in the lower right corner (**Open the check box**), and then go to the list to select the file we want to download - -3. Select the files we need to download. After selecting, there will be a row of buttons. Select the second option on the right. One is sent to Aria2. - - If you use the webpage to modify the **`user-agent`**, you do not need to configure the `UA` of **Aria2**, and you can download it directly to Aria2 - -::: - -::: zh-CN +### Aria2 1. 先照着下图设置好 **`"User-Agent"`**,然后在**右下角**的按钮选项,点击**齿轮**(本地设置),配置好参数 @@ -495,29 +216,7 @@ Example 2: Another plugin method covering 360, Chrome, Edge: **https://youtu.be/ 如果你使用了网页修改 **`"User-Agent"`**,可以不配置 **Aria2** 的`UA`,直接推送到Aria2也能下载 -::: - -### Motrix { lang="en" } - -### Motrix { lang="zh-CN" } - -::: en - -1. First follow the figure below to set the **`user-agent" `**, and then click the button option in the lower right corner, click ** gear ** (local settings) to configure the parameters well - - ![motrix-ua](/img/drivers/baidu/motrix-ua.png) - -2. Then open the third button option in the lower right corner (**Open the check box**), and then go to the list to select the file we want to download - -3. Select the files we need to download. After selecting, there will be a row of buttons. Select the second option on the right. One is sent to Aria2. - - If you use the webpage to modify the **`user-agent`**, you do not need to configure the `UA` of **Motrix**, and you can download it directly to Aria2 - -- Motrix Download link:[Motrix Official website](https://motrix.app/),[Motrix-GitHub](https://github.com/agalwood/Motrix) - -::: - -::: zh-CN +### Motrix 1. 先照着下图设置好 **`"User-Agent"`**,然后在**右下角**的按钮选项,点击**齿轮**(本地设置),配置好参数 @@ -531,29 +230,8 @@ Example 2: Another plugin method covering 360, Chrome, Edge: **https://youtu.be/ - Motrix下载链接:[Motrix官网](https://motrix.app/),[Motrix-GitHub](https://github.com/agalwood/Motrix) -::: - -## Upload Config { lang="en" } - -## 上传配置 { lang="zh-CN" } +## 上传配置 -::: en -Official Documentation: [百度网盘开放平台 - 上传 - 能力说明](https://pan.baidu.com/union/doc/3ksg0s9ye) - -> ~~Baidu Netdisk requires that each slice be uploaded within 30 seconds, so excessively high concurrency during file uploads may result in a significant number of failures.~~ -> Currently (November 2025), Baidu Netdisk appears to have significantly relaxed the timeout limit for uploading slices. -> Tests show that uploading single slice is now allowed to exceed 30 minutes. -> Therefore, after extending the timeout duration, the failure rate for batch uploading large files should be greatly reduced (even if low upload bandwidth). - -- Upload Threads: The number of slices to upload concurrently. -- Upload API: The domain endpoint used for uploading. -- Use dynamic upload API: Dynamically fetch upload domain, generally can achieve maximum upload speed. When enabled, the `Upload API` setting will be used as a fallback if failed to get online. This is enabled by default. -- Custom Upload Slice Size: Allows you to specify the size of each slice. Note that there are limitations, and this feature is available to VIPs only. -- Low Bandwidth Upload Mode: Attempts to address the frequent `Client.Timeout exceeded while awaiting headers` errors encountered in low upload bandwidth scenarios (e.g., residential broadband). When enabled, it uses the smallest possible slice size. - -::: - -::: zh-CN 官方文档:[百度网盘开放平台 - 上传 - 能力说明](https://pan.baidu.com/union/doc/3ksg0s9ye) > ~~百度网盘要求在 30s 内完成单个分片的上传,所以上传文件时并发过高可能会导致大量失败。~~ @@ -565,36 +243,7 @@ Official Documentation: [百度网盘开放平台 - 上传 - 能力说明](https - 自定义上传分片大小:用于指定分片大小,有限制,仅会员可用 - 低带宽上传模式:尝试解决低上传带宽场景(如家宽)下,频繁出现 `Client.Timeout exceeded while awaiting headers` 的问题。开启后会使用尽可能小的分片大小。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[local proxy]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[302]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -614,5 +263,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/baidu_photo.md b/pages/guide/drivers/baidu_photo.md index 28723afcd..93fc7bb24 100644 --- a/pages/guide/drivers/baidu_photo.md +++ b/pages/guide/drivers/baidu_photo.md @@ -1,61 +1,22 @@ --- -title: - en: Baidu Photo - zh-CN: 一刻相册 -icon: iconfont icon-state -# This control sidebar order top: 689 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 一刻相册 + ## Cookie -::: en -Log in to [Baidu Photo](https://photo.baidu.com), open F12, and find any request containing the `Cookie` value. Copy it. -::: - -::: zh-CN 登录 [一刻相册](https://photo.baidu.com) 打开F12随意找到一个携带 `Cookie` 值的请求进行复制就可以 -::: ![](/img/drivers/baidu/yike_cookie.png) -## Album ID { lang="en" } - -## 相薄ID { lang="zh-CN" } - -::: en - -**When left blank, all albums in the root directory are displayed by default.** - -If you want to mount a single album, fill in the following: - -- The **Album ID** should be: {album_id}|{tid} - Example: `4021858707431029901|316519298447849660` - - **{album_id}**: After entering the album you want to mount, check the top URL for the ID after `/album`. This is the **{album_id}**. - - Example: [https://photo.baidu.com/photo/web/album/4021858707431029901](https://photo.baidu.com/photo/web/album/4021858707431029901) - - **4021858707431029901** is the **{album_id}** - - - **{tid}**: Access [this link](https://photo.baidu.com/youai/album/v1/list?limit=1000) to obtain the **{tid}** - - Once on the page, press `Ctrl+F` and search for the ID above. A few lines below you’ll find the corresponding **{tid}**. +## 相薄ID -::: - -::: zh-CN **默认为空时,直接显示根目录全部相册.** 如果挂载单个相册时需填写如下内容 @@ -68,59 +29,15 @@ If you want to mount a single album, fill in the following: - `{tid}`:访问 **https://photo.baidu.com/youai/album/v1/list?limit=1000** 获取 - 进入界面后`Ctrl+F`搜索上面的ID,在下面数几行就可以看到对应的 `{tid}` -::: - -## Display Type { lang="en" } - -## 展示类型 { lang="zh-CN" } - -::: en -Choose according to your needs. -::: +## 展示类型 -::: zh-CN 根据自己的需求选择 -::: -## Delete Source Files { lang="en" } +## 删除源文件 -## 删除源文件 { lang="zh-CN" } - -::: en -By default, it only removes the album, not permanently deletes the files. If you enable this option, the files will be permanently deleted after removal. Be cautious when enabling this. -::: - -::: zh-CN 默认只是移除相册,并非真正删除,如果开启此选项删除文件后将会彻底删除,谨慎开启 -::: - -## The default download method used { lang="en" } -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -140,5 +57,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/chaoxing.md b/pages/guide/drivers/chaoxing.md index 51daf368c..c32bb1ea8 100644 --- a/pages/guide/drivers/chaoxing.md +++ b/pages/guide/drivers/chaoxing.md @@ -1,110 +1,37 @@ --- -title: - en: ChaoxingxingGroupCloud - zh-CN: 超星星小组盘 -icon: iconfont icon-state -# This control sidebar order top: 678 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - +# 超星星小组盘 -::: en -chaoxingxing group cloud is also affiliated with chaoxingxing. After logging in to chaoxingxing, you can see a group under the left sidebar. + -Official website link:**https://i.chaoxing.com** -::: -::: zh-CN 超星星小组盘也是隶属于超星星,登录超星星后在左侧侧边栏下方能看到一个小组。 官网链接:**https://i.chaoxing.com** -::: -### Username Password { lang="en" } +### 用户名 密码 -### 用户名 密码 { lang="zh-CN" } - -::: en -chaoxingxing account and password -::: -::: zh-CN 超星星的帐号密码 -::: - -### Bbsid { lang="en" } -### Bbsid { lang="zh-CN" } +### Bbsid -::: en -Follow the example process below to obtain - -![](/img/drivers/chaoxing/chaoxing_bbsid.png) -::: -::: zh-CN 按照下面示例流程获取 ![](/img/drivers/chaoxing/chaoxing_bbsid.png) -::: - -### Root Folder ID { lang="en" } -### 根文件夹ID { lang="zh-CN" } +### 根文件夹ID -::: en -The default is `-1`. For other subfolder ID acquisition methods, see the example below. - -- Search the keyword `getResourceList` in the request. For convenience, we can directly search `list` and see it. Then we enter the folder we need to mount. After entering the folder, we can find the `folder id by looking at the request.` - - ![](/img/drivers/chaoxing/chaoxing_folder_id.gif) - -::: -::: zh-CN 默认为`-1`,其它子文件夹ID获取方式看下图示例 - 在请求中搜索关键词`getResourceList`,为了方便我们可以直接搜索 `list`也可以看到,然后我们进入我们需要挂载的文件夹,进文件夹后再去看请求就可以找到`子文件夹ID` ![](/img/drivers/chaoxing/chaoxing_folder_id.gif) -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -122,5 +49,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/chunk.md b/pages/guide/drivers/chunk.md index 38059cc03..13de52d5b 100644 --- a/pages/guide/drivers/chunk.md +++ b/pages/guide/drivers/chunk.md @@ -1,72 +1,21 @@ --- -title: - en: Chunk - zh-CN: 分块(Chunk) -icon: iconfont icon-state top: 995 categories: - guide - drivers -tag: - - Storage - - Guide -sticky: true -star: true --- -::: en -The Chunk is used to split large files into multiple file chunks. Each chunk is stored as an independent file on the backend driver. +# 分块(Chunk) -The size of each file chunk can be manually configured by the user. Except for the last chunk of each file—which is smaller than the configured size—all other chunks are always equal to the specified size. - -A chunked file is stored as a folder on the backend driver, containing all its chunks. - -::: - -::: zh-CN 分块驱动用于将大文件拆分为多个文件块,每个文件块以一个独立文件的形式存储在后端驱动上。 每个文件块的大小可由用户手动设定,除了每个文件的最后一个文件块的大小小于该设定值外,其余文件块的大小总是等于该设定值。 被分块的文件会以文件夹的形式存在于后端驱动上,文件夹内是该文件的所有分块。 -::: - ![chunk_example](/img/drivers/chunk/example.png) -## Setup Instructions { lang="en" } - -## 配置说明 { lang="zh-CN" } - -::: en - -- **Remote path:** The real storage path of the chunked files. This path must be the root path or a subpath of another driver. -- **Part size:** Maximum chunk size / Size of chunks except the last, in bytes. -- **Chunk large file only:** Whether to only chunk files larger than Part size. - - When disabled: - ![chunk_large_only_disabled](/img/drivers/chunk/chunk_large_only_disabled.png) - - When enabled: - ![chunk_large_only_enabled](/img/drivers/chunk/chunk_large_only_enabled.png) -- **Chunk prefix:** The prefix for the names of chunk folders stored on the backend driver, representing a chunked file. This is used to identify whether a folder is a regular folder or a chunked file storing folder, and therefore cannot be empty. -- **Custom ext:** Custom suffix for chunk names, used to bypass limitations of certain drivers. - - When left empty:![custom_ext_disabled](/img/drivers/chunk/custom_ext_disabled.png) - - When setting to `.jpg`:![custom_ext_enabled](/img/drivers/chunk/custom_ext_enabled.png) - -::: warning TIP -Once the driver is created, Custom ext should not be modified again, otherwise previously uploaded chunked files will become unrecognizable. - -If modification is necessary, you must manually change the suffix of all chunks on the backend driver afterward. -::: - -::: en - -- **Store hash:** Whether to store the hashes of chunked files in the chunk folder as well. - This feature does not actively compute hashes; it only stores the hashes that the file already carries when uploading. -- **Num list workers:** When handling `List` requests, if a folder contains chunk folders, it is necessary to further list those chunk folders to retrieve information such as file size and hash value. Using multiple threads can speed up this process. A higher number of threads consumes more CPU and bandwidth resources but also increases speed. It is recommended to be disabled (set to 1) when the backend driver has API rate limiting. - -::: - -::: zh-CN +## 配置说明 - **远程存储路径:** 分块后文件的真实存储路径,该路径必须是其它驱动的根路径或子路径。 - **分块大小:** 文件块的最大大小 / 除最后一个文件块外文件块的大小,单位:字节。 @@ -88,56 +37,12 @@ If modification is necessary, you must manually change the suffix of all chunks 如必须修改,修改后需要在后端驱动手动修改所有文件块的后缀名。 ::: -::: zh-CN - - **存储哈希值:** 是否在分块文件夹中同时存储完整文件的哈希值。 该功能不会主动计算文件的哈希值,只会存储文件在上传时就已经携带的哈希值。 - **列举分块文件夹线程数:** 处理`List`请求时,如果文件夹中有分块文件夹,需要继续列举分块文件夹以获取文件大小、哈希值等信息,此时使用多线程可以加速这一过程。线程数越大,消耗 CPU 和带宽资源越多,速度越快。后端驱动具有 API 限速时建议关闭(设为1)。 -::: - -## Use in combination with Crypt { lang="en" } - -## 与 Crypt 结合使用 { lang="zh-CN" } - -::: en -If you need to both chunk and encrypt files, it is recommended to **encrypt first, then chunk**. Specifically, set Remote path of the [Crypt](/guide/drivers/crypt) driver to the mount path of the **Chunk** driver, and set Remote path of the **Chunk** driver to the actual storage path of the files. - -- Best practice: - -```mermaid -graph LR - File[Upload stream] --> Crypt - Crypt --> Chunk - Chunk -->|chunk1| Backend[Backend driver] - Chunk -->|chunk2| Backend - Chunk -->|chunk3| Backend -``` - -- Bad practice: +## 与 Crypt 结合使用 -```mermaid -graph LR - File[Upload stream] --> Chunk - Chunk -->|chunk1| Crypt1[Encryption algorithm] - Chunk -->|chunk2| Crypt2[Encryption algorithm] - Chunk -->|chunk3| Crypt3[Encryption algorithm] - Crypt1 -->|encrypted chunk 1| Backend[Backend driver] - Crypt2 -->|encrypted chunk 2| Backend - Crypt3 -->|encrypted chunk 3| Backend - subgraph Crypt - Crypt1 - Crypt2 - Crypt3 - end -``` - -**Reason**: The bad practice involves storing a series of encryption metadata in each file chunk, which consumes more space. It also fails to guarantee that the chunks adhere to the user-specified maximum size, thereby undermining the purpose of file chunking. - -To perform emergency recovery on an encrypted file chunked according to the best practice, you simply need to concatenate all the chunks in order and then proceed with recovery. -::: - -::: zh-CN 如果既需要对文件进行分块,又需要对文件进行加密,推荐**先加密后分块**,即将 [Crypt](/guide/drivers/crypt) 驱动的远程存储路径设为**分块**驱动的挂载路径,将**分块**驱动的远程存储路径设为文件真实存储路径。 - 正确实践: @@ -172,49 +77,9 @@ graph LR **原因**:错误实践的这种做法在每个文件块中都存储了一系列加密元信息,占用空间更多,也不能保证文件块具有用户给定的最大大小,失去了对文件分块的意义。 如果需要紧急恢复按推荐做法分块的加密文件,只需要按顺序直接拼接所有分块,再进行恢复即可。 -::: - -## Use in combination with Alias { lang="en" } - -## 与别名结合使用 { lang="zh-CN" } -::: en -If you need to store chunked files across multiple load-balanced drivers, you can combine this with the [Alias](/guide/drivers/alias) driver. For details, refer to [Load Balancing / Load balancing by file chunks](/guide/advanced/balance#load-balancing-by-file-chunks). +## 与别名结合使用 -It's important to note that, unlike the approach used with Crypt, when combining with Alias, the process should be **chunk first, then load balance**. This means setting the Remote path of the **Chunk** driver to the mount path of the **Alias** driver, and setting the paths within the **Alias** driver to the actual file storage paths. - -- Best Practice: - -```mermaid -graph LR - File[Upload Stream] -->|File1| Chunk - File -->|File2| Chunk - Chunk -->|File1 Block1, File1 Block2, File1 Block3| Alias - Chunk -->|File2 Block1, File2 Block2| Alias - Alias -->|File1 Block3| D1[Backend Driver 1] - Alias -->|File1 Block2| D2[Backend Driver 2] - Alias -->|File1 Block1| D3[Backend Driver 3] - Alias -->|File2 Block1| D1 - Alias -->|File2 Block2| D3 -``` - -- Bad Practice: - -```mermaid -graph LR - File[Upload Stream] -->|File1| Alias - File -->|File2| Alias - Alias -->|File1| Chunk1[Chunk 1] - Alias -->|File2| Chunk2[Chunk 2] - Chunk1 -->|File1 Block1| D1[Backend Driver 1] - Chunk1 -->|File1 Block2| D1 - Chunk1 -->|File1 Block3| D1 - Chunk2 -->|File2 Block1| D2[Backend Driver 2] - Chunk2 -->|File2 Block2| D2 -``` - -::: -::: zh-CN 如果需要将分块后的文件在多个负载均衡驱动上存储,可以与[别名](/guide/drivers/alias)结合使用,详见[负载均衡 / 按文件块负载均衡](/guide/advanced/balance#按文件块负载均衡)。 需要注意的是,与结合 Crypt 使用的方案不同,与别名结合使用时,应**先分块后负载均衡**,即将**分块**驱动的远程存储路径设为**别名**驱动的挂载路径,将**别名**驱动的路径设为文件真实存储路径。 @@ -248,5 +113,3 @@ graph LR Chunk2 -->|文件2块1| D2[后端驱动 2] Chunk2 -->|文件2块2| D2 ``` - -::: diff --git a/pages/guide/drivers/cloudflare-imgbed.md b/pages/guide/drivers/cloudflare-imgbed.md index f85ace542..85cd90341 100644 --- a/pages/guide/drivers/cloudflare-imgbed.md +++ b/pages/guide/drivers/cloudflare-imgbed.md @@ -1,175 +1,67 @@ --- -title: - en: CloudFlare-ImgBed - zh-CN: CloudFlare 图床 -icon: iconfont icon-state top: 700 categories: - guide - drivers --- - +# CloudFlare 图床 -## 免责声明 { lang="zh-CN" } + -## Disclaimer { lang="en" } +## 免责声明 -::: zh-CN ::: warning 本驱动仅提供与 CloudFlare-ImgBed 后端交互的接口,不对该图床程序本身及其存储渠道的可用性、稳定性负责。 用户需自行决定并配置所使用的存储渠道(如 Cloudflare R2、HuggingFace、S3、Telegram 等),并应严格遵守相关平台及服务提供商的服务条款与使用政策。请勿利用此驱动进行任何违反法律法规、侵犯他人权益或滥用平台资源(如大量消耗免费额度、违规存储分发受版权保护的内容等)的行为。 因用户配置不当、违反平台政策或滥用服务而导致的一切后果(包括但不限于账号封禁、数据丢失、服务中断等),均由用户自行承担,与 OpenList 及其开发者无关。 ::: -::: en -::: warning -This driver only provides an interface to interact with the CloudFlare-ImgBed backend and is not responsible for the availability and stability of the image hosting program itself or its storage channels. -Users are solely responsible for determining and configuring the storage channels used (e.g., Cloudflare R2, HuggingFace, S3, Telegram) and must strictly comply with the Terms of Service and acceptable use policies of the relevant platforms and service providers. Do not use this driver for any illegal activities, rights infringement, or abuse of platform resources (e.g., excessive consumption of free quotas, illegal storage/distribution of copyrighted content). -All consequences arising from improper configuration, violation of platform policies, or service abuse (including but not limited to account suspension, data loss, service interruption, etc.) shall be borne by the user. OpenList and its developers shall not be held liable. -::: -## 1. 准备工作 { lang="zh-CN" } +## 1. 准备工作 -## 1. Preparation { lang="en" } - -::: zh-CN 配置该驱动前,需要先部署你自己的 CloudFlare-ImgBed 后端。 项目地址:[MarSeventh/CloudFlare-ImgBed](https://github.com/MarSeventh/CloudFlare-ImgBed) 部署完成后,登录后端管理面板(`https://你的域名/dashboard`),并在系统设置中确保至少配置了一个存储渠道(如 HuggingFace、Cloudflare R2、S3、Telegram 等)。 -::: -::: en -Before configuring this driver, you need to deploy your own CloudFlare-ImgBed backend. -Project address: [MarSeventh/CloudFlare-ImgBed](https://github.com/MarSeventh/CloudFlare-ImgBed) -After deployment, log in to the backend dashboard (`https://your-domain/dashboard`), and ensure that at least one storage channel (e.g., HuggingFace, Cloudflare R2, S3, Telegram) is configured in the system settings. -::: -## 2. 在 OpenList 中添加 { lang="zh-CN" } +## 2. 在 OpenList 中添加 -## 2. Add in OpenList { lang="en" } +### 挂载路径 -### 挂载路径 { lang="zh-CN" } - -### Mount Path { lang="en" } - -::: zh-CN 填入你希望挂载到的路径,例如 `/imgbed`。 -::: -::: en -Enter the path you want to mount to, for example `/imgbed`. -::: - -### 根目录路径 { lang="zh-CN" } -### Root Folder Path { lang="en" } +### 根目录路径 -::: zh-CN 默认为 `/`,留空即可。 -::: -::: en -Default is `/`, can be left empty. -::: - -### 后端 API 地址 { lang="zh-CN" } -### Address { lang="en" } +### 后端 API 地址 -::: zh-CN 填入你部署的图床服务地址,例如 `https://img.example.com`。无需在末尾添加 `/`。 -::: -::: en -Enter the address of your deployed image hosting service, e.g., `https://img.example.com`. No need to add `/` at the end. -::: - -### 认证令牌 { lang="zh-CN" } -### Token { lang="en" } +### 认证令牌 -::: zh-CN 填入图床后端系统设置中生成的 API Token。 ![](/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png) -::: -::: en -Enter the API Token generated in the backend system settings. -![](/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png) -::: - -### 普通文件渠道名称 { lang="zh-CN" } -### Small Channel Name { lang="en" } +### 普通文件渠道名称 -::: zh-CN 通常用于上传小于 20MB 的文件。填入你在图床后端配置的渠道名称(如 `my cfr2`、`my telegram` 等)。 -::: -::: en -Typically used for uploading files smaller than 20MB. Enter the channel name configured in your backend (e.g., `my cfr2`, `my telegram`). -::: - -### 大文件渠道名称 { lang="zh-CN" } -### Large Channel Name { lang="en" } +### 大文件渠道名称 -::: zh-CN 用于上传大于 20MB 的文件。建议配置支持大文件的渠道(如 `huggingface`)。留空则大文件会回退到普通渠道上传。 -::: -::: en -Used for uploading files larger than 20MB. It is recommended to configure a channel that supports large files (e.g., `huggingface`). If left blank, large files will fall back to the small channel for upload. -::: - -### 大文件渠道类型 { lang="zh-CN" } - -### Large Channel Type { lang="en" } +### 大文件渠道类型 -::: zh-CN 根据大文件渠道选择对应的类型: - `huggingface`:使用 HuggingFace LFS 直传(支持秒传和分片) - `telegram` / `cfr2` / `s3` / `discord`:使用图床后端的分片上传接口 - ::: - ::: en - Select the corresponding type based on your large file channel: -- `huggingface`: Uses HuggingFace LFS direct upload (supports instant upload and chunking) -- `telegram` / `cfr2` / `s3` / `discord`: Uses the backend chunked upload API - ::: -### 上传线程数 { lang="zh-CN" } +### 上传线程数 -### Upload Thread { lang="en" } - -::: zh-CN HuggingFace 分片直传的并发线程数,默认为 `3`,最大为 `32`。网络条件好时可适当增大。 -::: -::: en -Concurrent thread count for HuggingFace chunked direct upload, default is `3`, max is `32`. Can be increased if network conditions are good. -::: -## 默认使用的下载方式 { lang="zh-CN" } - -## The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -189,5 +81,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/cloudreve.md b/pages/guide/drivers/cloudreve.md index 7f1595127..ead05ef3b 100644 --- a/pages/guide/drivers/cloudreve.md +++ b/pages/guide/drivers/cloudreve.md @@ -1,57 +1,20 @@ --- -title: - en: Cloudreve V3 - zh-CN: Cloudreve V3 -icon: iconfont icon-state -# This control sidebar order top: 498 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -## Parameters { lang="en" } +# Cloudreve V3 -## 参数说明 { lang="zh-CN" } +## 参数说明 -### Address { lang="en" } +### 地址 -### 地址 { lang="zh-CN" } - -::: en -The address of the Cloudreve V3 server, such as: `https://www.example.com`. It is better to remove the slash `/` after it. -::: -::: zh-CN Cloudreve V3 服务器的地址,如:`https://www.example.com`,最好删掉后面的斜线`/`。 -::: - -### Authentication { lang="en" } - -### 鉴权 { lang="zh-CN" } - -::: en -Cloudreve V3 supports authentication using Cookie. The mounting authentication methods are divided into: -1. `Username` + `Password`: Automatically uses the login interface to obtain Cookie, there might be issues with CAPTCHA. -2. `Cookie` Only: Can be temporarily used but will expire and cannot be renewed. Parameters can be found from browser requests or Local Storage. -3. [Reference](../drivers/common.html#Reference): Fill in `ref:/{mount path}` in `Remark`: Reference authentication, tokens, etc., from "Mounted Storage". +### 鉴权 -::: tip Steps to get cookie: -Only the string following cloudreve-session= is required. -::: - -::: zh-CN Cloudreve V3 使用 Cookie 进行鉴权,这意味着挂载鉴权方式分为: 1. `用户名`+`密码`:会自动使用登录接口获取 Cookie,存在验证码问题 @@ -64,55 +27,20 @@ Cloudreve V3 使用 Cookie 进行鉴权,这意味着挂载鉴权方式分为 ![cloudreve_get_cookie](/img/drivers/cloudreve/cloudreve_get_cookie.png) -### Root Folder Path { lang="en" } - -### 根文件夹路径 { lang="zh-CN" } +### 根文件夹路径 -::: en -Default is `/`, which can be obtained from the `?path=` in the web link. The obtained parameters may need to be [URL decoded](https://www.google.com/search?q=URL+decode). -::: -::: zh-CN 默认为:`/`,可从网页链接的 `?path=` 中获取。获取到的参数可能需要 [URL 解码](https://www.bing.com/search?q=URL+%E8%A7%A3%E7%A0%81)。 -::: -### Enable Folder Size { lang="en" } +### 启用缩略图和文件夹大小 -### 启用缩略图和文件夹大小 { lang="zh-CN" } - -::: en -Enable Cloudreve V3 server to calculate the size of each folder and generate thumbnails for each file. Enabling this feature may cause server errors or slow performance, and it is `disabled` by default. -::: -::: zh-CN 让 Cloudreve V3 服务端统计每个文件夹的大小并为每个文件生成略缩图,启用可能会造成服务端报错、运行缓慢,默认禁用。 -::: -### Custom UA { lang="en" } +### 自定义 UA -### 自定义 UA { lang="zh-CN" } - -::: en -Used to customize the `User-Agent` header information used in requests. Leaving it blank will use OpenList default settings. -::: -::: zh-CN 用于自定义请求使用的 `User-Agent` 头部信息。留空为 OpenList 默认。 -::: -## Upload { lang="en" } - -## 上传 { lang="zh-CN" } - -::: en -The supported storage policies for uploading are as follows: - -- Local storage -- Slave storage -- OneDrive -- S3 +## 上传 -Upload storage policies can be set within Cloudreve V4 web interface by entering the corresponding folder (requires the server to be Cloudreve Pro). -::: - -::: zh-CN 支持上传到的存储策略如下: - 本机存储 @@ -121,35 +49,8 @@ Upload storage policies can be set within Cloudreve V4 web interface by entering - S3 上传的存储策略请在 Cloudreve V3 网页端进入相应文件夹中进行设置(需要服务端为 Cloudreve Pro)。 -::: -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -169,5 +70,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/cloudreve_v4.md b/pages/guide/drivers/cloudreve_v4.md index 3a3d87773..5c99c780e 100644 --- a/pages/guide/drivers/cloudreve_v4.md +++ b/pages/guide/drivers/cloudreve_v4.md @@ -1,48 +1,20 @@ --- -title: - en: Cloudreve V4 / Share - zh-CN: Cloudreve V4 / 分享 -icon: iconfont icon-state -# This control sidebar order top: 497 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags --- -## Parameter Explanation { lang="en" } +# Cloudreve V4 / 分享 -## 参数说明 { lang="zh-CN" } +## 参数说明 -### Address { lang="en" } +### 地址 -### 地址 { lang="zh-CN" } - -::: en -The address of the Cloudreve V4 server, such as: `https://www.example.com`. It is better to remove the slash `/` after it. -::: -::: zh-CN Cloudreve V4 服务器的地址,如:`https://www.example.com`,最好删掉后面的斜线`/`。 -::: - -### Authentication { lang="en" } - -### 鉴权 { lang="zh-CN" } - -::: en -Cloudreve V4 supports authentication using Token. The mounting authentication methods are divided into: -1. `Username` + `Password`: Automatically uses the login interface to obtain Access Token and Refresh Token, there might be issues with CAPTCHA. -2. `Refresh Token` Only: Automatically uses the refresh interface to renew Access Token and Refresh Token, parameters can be found from browser requests or Local Storage. -3. `Access Token` Only: Can be temporarily used but will expire and cannot be renewed. -4. None: Anonymous user, suitable for public sharing. -5. [Reference](../drivers/common.html#reference): Fill in `ref:/{mount path}` in `Remark`: Reference authentication, tokens, etc., from "Mounted Storage". +### 鉴权 -::: - -::: zh-CN Cloudreve V4 使用 Token 进行鉴权,这意味着挂载鉴权方式分为: 1. `用户名`+`密码`:会自动使用登录接口获取 Access Token 和 Refresh Token,存在验证码问题 @@ -51,179 +23,65 @@ Cloudreve V4 使用 Token 进行鉴权,这意味着挂载鉴权方式分为: 4. 无:匿名用户,适用于公开分享 5. [引用](../drivers/common.html#引用):`备注`填写 `ref:/{挂载路径}`,从 `已挂载的存储` 中引用认证、令牌等 -::: - -### Steps to get tokens { lang="en" } - -### Token 获取方法 { lang="zh-CN" } +### Token 获取方法 -#### From Local Storage { lang="en" } +#### 本地存储 -#### 本地存储 { lang="zh-CN" } - -::: en -::: tip -There may be multiple sessions; please select the one you want to mount. -::: -::: zh-CN ::: tip 可能有多个会话,请选择要挂载的那个。 ::: -::: en -![cloudreve_v4_token_local_storage](/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png) -::: - -::: zh-CN ![cloudreve_v4_token_local_storage](/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png) -::: - -#### From Network { lang="en" } -#### 浏览器请求 { lang="zh-CN" } - -::: en -::: tip -You may need to log in anew to see this request. -::: +#### 浏览器请求 -::: zh-CN ::: tip 可能需要全新登录才能看到这个请求。 ::: -::: en -![cloudreve_v4_token_resp](/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png) -::: -::: zh-CN -![cloudreve_v4_token_resp](/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png) -::: -### Root Folder Path { lang="en" } +![cloudreve_v4_token_resp](/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png) -### 根文件夹路径 { lang="zh-CN" } +### 根文件夹路径 -::: en -Cloudreve V4 uses a custom URI as the path, which can be obtained from the `?path=` in the web link. The obtained parameters may need to be [URL decoded](https://www.google.com/search?q=URL+decode). -::: -::: zh-CN Cloudreve V4 采用自定义 URI 作为路径。可从网页链接的 `?path=` 中获取。获取到的参数可能需要 [URL 解码](https://www.bing.com/search?q=URL+%E8%A7%A3%E7%A0%81)。 -::: - -#### Mounting My Files { lang="en" } -#### 挂载「我的文件」 { lang="zh-CN" } +#### 挂载「我的文件」 -::: en -Default is `cloudreve://my/`, listing user files. -::: -::: zh-CN 默认为 `cloudreve://my/`,列出用户文件。 -::: -#### Mounting Share { lang="en" } +#### 挂载「分享」 -#### 挂载「分享」 { lang="zh-CN" } - -::: en -Supports mounting folder-type shares, the path should be filled as: `cloudreve://{ShareID}@share/`. - -- ShareID is the parameter after `/s/` in the share link. -- Currently, Cloudreve V4 does not support creating password-protected shares. However, if the data is migrated from V3, the previously created sharing password will be retained. The URI of the share link with the password (`/s/{shareID}/{sharePassword}`) is `cloudreve://{shareID}:{sharePassword}@share`. - -::: - -::: zh-CN 支持挂载文件夹类型的分享,路径填写为:`cloudreve://{分享ID}@share/`。 - 分享ID为分享链接 `/s/` 后面的参数。 - 目前 Cloudreve V4 不支持「创建带密码的分享」。但如果数据迁移自 V3,先前创建的分享密码会被保留。带密码的分享链接(`/s/{分享ID}/{分享密码}`),对应的 URI 为 `cloudreve://{分享ID}:{分享密码}@share`。 -::: - -### Enable Folder Size { lang="en" } - -### 启用文件夹大小 { lang="zh-CN" } +### 启用文件夹大小 -::: en -Enable Cloudreve V4 server to calculate the size of each folder. Enabling this feature may cause server errors or slow performance, and it is `disabled` by default. -::: -::: zh-CN 让 Cloudreve V4 服务端统计每个文件夹的大小,启用可能会造成服务端报错、运行缓慢,默认禁用。 -::: -### Enable Thumbnails { lang="en" } +### 启用缩略图 -### 启用缩略图 { lang="zh-CN" } - -::: en -Enable Cloudreve V4 server to generate thumbnails for each file. Enabling this feature may cause server errors or slow performance, and it is `disabled` by default. -::: -::: zh-CN 让 Cloudreve V4 服务端为每个文件生成略缩图,启用可能会造成服务端报错、运行缓慢,默认禁用。 -::: -### Enable Version Upload { lang="en" } +### 启用版本上传 -### 启用版本上传 { lang="zh-CN" } - -::: en -Enable overwrite upload and keep the previous version, which consumes extra space. Disabled by default, delete the existing file before overwriting upload. -::: -::: zh-CN 启用覆盖上传并保留之前的版本,需要消耗额外的空间。默认禁用,为覆盖上传前删除已有的文件。 -::: -### Custom UA { lang="en" } +### 自定义 UA -### 自定义 UA { lang="zh-CN" } - -::: en -Used to customize the `User-Agent` header information used in requests. Leaving it blank will use OpenList default settings. -::: -::: zh-CN 用于自定义请求使用的 `User-Agent` 头部信息。留空为 OpenList 默认。 -::: -### Sorting { lang="en" } +### 排序 -### 排序 { lang="zh-CN" } - -::: en -Supports changing the sorting parameters when requesting lists. -::: -::: zh-CN 支持更改请求列表时排序的参数。 -::: - -### Hide files currently being uploaded { lang="en" } -### 隐藏正在上传中的文件 { lang="zh-CN" } +### 隐藏正在上传中的文件 -::: en -Filter files with upload sessions (in progress) based on metadata information, size 0, unable to download -::: -::: zh-CN 根据 Metadata 信息进行过滤带有上传会话(上传中)的文件,大小为 0,无法下载 -::: - -## Upload { lang="en" } - -## 上传 { lang="zh-CN" } - -::: en -The supported storage policies for uploading are as follows: - -- Local storage -- Slave storage -- OneDrive -- S3 -- KS3 -Upload storage policies can be set within Cloudreve V4 web interface by entering the corresponding folder (requires the server to be Cloudreve Pro). +## 上传 -If upload permissions are enabled for shares, uploads are supported. It requires the server to check "Enhance anonymous user permissions" for the user group (requires the server to be Cloudreve Pro). -::: -::: zh-CN 支持上传到的存储策略如下: - 本机存储 @@ -235,35 +93,8 @@ If upload permissions are enabled for shares, uploads are supported. It requires 上传的存储策略请在 Cloudreve V4 网页端进入相应文件夹中进行设置(需要服务端为 Cloudreve Pro)。 如果分享开启了上传权限,支持上传。需要服务端为用户组勾选「提升匿名用户权限」(需要服务端为 Cloudreve Pro)。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -283,5 +114,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/cnb_releases.md b/pages/guide/drivers/cnb_releases.md index b1ce2edde..33cc7c1e7 100644 --- a/pages/guide/drivers/cnb_releases.md +++ b/pages/guide/drivers/cnb_releases.md @@ -1,30 +1,16 @@ --- -title: - en: CNB Releases - zh-CN: CNB 发行版 categories: - guide - drivers top: 397 --- -https://cnb.cool/ - -## Known issues { lang="en" } +# CNB 发行版 -## 已知问题 { lang="zh-CN" } - -::: en -CNB releases are not a standard file system and have some unresolved issues. Please do not submit feedback for these. +https://cnb.cool/ -1. An empty repository needs to be initialized first. -2. Subdirectories are not supported under Tag / Release directories. -3. The OpenAPI upload returns `expires_in_sec` as only 10 seconds. Uploading large files may time out; after time out, the upload continues but returns `invalid token`. Therefore, a local timeout mechanism is added to automatically stop uploading when the set time is reached, avoiding failed uploads and wasted bandwidth. -4. `Move`, `Copy`, and `Rename` operations are not supported. -5. Only when `UseTagName` is disabled, renaming can be done by modifying the Release name. +## 已知问题 -::: -::: zh-CN CNB 发行版非正常文件系统,存在一些无法解决的问题,请勿反馈。 1. 空仓库需要先初始化 @@ -33,84 +19,27 @@ CNB 发行版非正常文件系统,存在一些无法解决的问题,请勿 4. 不支持 `移动`、`复制`、`重命名` 操作 5. 仅支持在关闭 `使用Tag名称` 时,通过修改 Release 的名称进行重命名 -::: - -## Parameters { lang="en" } - -## 参数 { lang="zh-CN" } - -### Repo { lang="en" } +## 参数 -### 仓库 { lang="zh-CN" } +### 仓库 -::: en -Only one repository can be specified. To reuse a token, please use the [Reference](common.md#reference) feature. -::: -::: zh-CN 仅支持填写一个仓库。如需复用 Token,请使用 [引用](common.md#引用) 功能。 -::: -### Token { lang="en" } +### 访问令牌 -### 访问令牌 { lang="zh-CN" } - -::: en -Access token. No need to include `Bearer`. Supports the [Reference](common.md#reference) feature. - -How to obtain: After logging in, go to `Personal Settings` - [Access Token](https://cnb.cool/profile/token) -> `Add Access Token`. - -For listing, grant the `repo-code:r` permission. For modifications, grant the `repo-code:rw` permission. -::: -::: zh-CN 访问令牌。无需填写 `Bearer`。支持 [引用](common.md#引用) 功能。 获取方法:登录后,进入 `个人设置` - [访问令牌](https://cnb.cool/profile/token) -> `添加访问令牌`。 对于获取列表,需授予 `repo-code:r` 权限。对于修改,需授予 `repo-code:rw` 权限。 -::: - -### UseTagName { lang="en" } -### 使用Tag名称 { lang="zh-CN" } +### 使用Tag名称 -::: en -Use the original Tag name instead of the Release name. - -By default, this is disabled and the Release name is used, which supports renaming. Tag names cannot be changed once created. When creating a new folder for the first time, a Tag with the folder name will be published. Renaming an existing Release will modify the Release name. -::: -::: zh-CN 使用原始的 Tag 名称而不是 Release 名称。 默认关闭,使用 Release 名称,支持重命名。Tag 名称创建后无法更改。首次新建文件夹,会发布名为文件夹名称的 Tag,重命名已经存在的 Release,会修改 Release 的名称。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -130,5 +59,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/common.md b/pages/guide/drivers/common.md index 18e83d227..8c96ecac9 100644 --- a/pages/guide/drivers/common.md +++ b/pages/guide/drivers/common.md @@ -1,52 +1,20 @@ --- -title: - en: Common - zh-CN: 通用项 -icon: iconfont icon-state -# This control sidebar order top: 1000 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -## Mount Path { lang="en" } +# 通用项 -## 挂载路径 { lang="zh-CN" } +## 挂载路径 -::: en -The unique identifier for the mount point, the name displayed externally, and the location where it should be mounted. If you want to mount it to the root directory, please enter `/`. -::: - -::: zh-CN 挂载项的唯一标识,对外展示的名称,要挂载到的位置。如果要挂载到根目录,请填写 `/`。 -::: ![](/img/drivers/common/path_b.png#light) ![](/img/drivers/common/path_h.png#dark) -::: en -::: danger -You cannot use duplicate mount path names, otherwise, the following error will occur: - -```json -Failed to create storage in database: UNIQUE constraint failed: x_storages.mount_path -``` - -Solution: Use [aliases](./alias.md) to aggregate multiple mount points. -::: - -::: zh-CN ::: danger 不能使用重复的挂载路径名称,否则会报错: @@ -57,18 +25,6 @@ Failed create storage in database: UNIQUE constraint failed: x_storages.mount_pa 解决方法:使用 [别名](./alias.md) 对多个挂载项目进行聚合。 ::: -::: en -::: danger -The mount path name is a required field and cannot be left empty, or the following error will occur: - -```json -Key: 'Storage.MountPath' Error: Field validation for 'MountPath' failed on the 'required' tag -``` - -Solution: If you want to mount to the root directory, please enter `/`. -::: - -::: zh-CN ::: danger 挂载路径名称是必填项,不能为空,否则会报错: @@ -79,49 +35,16 @@ Key: 'Storage.MountPath' Error:Field validation for 'MountPath' failed on the 'r 解决方案:如果要挂载到根目录,请填写 `/`。 ::: -## Order { lang="en" } +## 序号 -## 序号 { lang="zh-CN" } - -::: en -When mounting multiple drives, this is used for sorting. The smaller the number, the further to the front. Negative numbers can also be used. -::: -::: zh-CN 当挂载多个驱动时,用于排序。越小越靠前。可以填写负数。 -::: - -## Remark { lang="en" } -## 备注 { lang="zh-CN" } +## 备注 -::: en -You can add notes for easier management. -::: -::: zh-CN 您可以添加备注,以方便管理。 -::: - -### Reference { lang="en" } - -### 引用 { lang="zh-CN" } - -::: en -Reference authentication, tokens, etc., from the **"Mounted Storage"** to enable sharing the same token between multiple cloud drives. - -Currently, the following cloud drives are supported: - -- 139Yun -- AliyundriveOpen -- 189CloudPC -- 123PanShare(ref 123Pan) -- Cloudreve V3 / V4 - -**How to use**: In the storage settings, set the first line of **Remark** to: **ref:/mount_path** -**Important**: `ref:/` should be in lowercase letters and symbols. -::: +### 引用 -::: zh-CN 从 `已挂载的存储` 中引用认证、令牌等,实现同一个 Token 多个网盘使用。 目前支持如下网盘: @@ -135,91 +58,30 @@ Currently, the following cloud drives are supported: **使用方法**:在存储设置中将`备注(Remark)`的第一行设置为:**ref:/挂载路径** **注意事项**:`ref:/` 为小写英文和符号 -::: ![](/img/drivers/ref_token.png) -## Enable signing { lang="en" } - -## 启用签名 { lang="zh-CN" } +## 启用签名 -::: en -Sign and encrypt files (no password required), only valid for this driver, if other signatures are not enabled and `signature all` and `meta-information encryption` are not set, others will not be signed. - -Usage scenario: I don't want to enable all signatures, and I don't want to set metadata encryption. I just want to sign and encrypt a certain driver to prevent it from being scanned. - -Scope of influence: `Settings-->Global-->Signature All` > `Metainformation Directory Encryption` > `Single Driver Signature`. -::: -::: zh-CN 对文件进行签名加密(不会需要密码),仅对本驱动生效,如果别的没启用签名也没设置`签名全部`和`元信息加密`其他的不会进行签名。 使用场景:不想开启全部签名,也不想设置元信息加密,只想对某驱动进行签名加密防止被扫。 影响范围:`设置-->全局-->签名所有` > `元信息目录加密` > `单驱动签名` -::: -## Disable index { lang="en" } +## 禁用索引 -## 禁用索引 { lang="zh-CN" } - -::: en -Allow users to disable storage indexing. - -- For example, if you enable `Ignore Index` in the index options, you no longer need to configure it after enabling `Disable Index`, which is more convenient. - -::: - -::: zh-CN 允许用户禁用存储索引。 - 例如索引选项中的`忽略索引`,启用`禁用索引`后不需要再去配置了,这样也更方便一些 -::: - -## Cache Expiration { lang="en" } - -## 缓存过期 { lang="zh-CN" } +## 缓存过期 -::: en -Cache time of directory structure. -::: -::: zh-CN 目录结构的缓存时间。 -::: -## Custom Cache Policies { lang="en" } - -## 自定义缓存策略 { lang="zh-CN" } - -::: en - -Cache time for directory paths (in minutes). - -You can customize the cache time for specific file paths using pattern matching. The configuration supports wildcard patterns: - -- `*` matches a **single** directory level. -- `**` matches **multiple** directory levels. - -Example configuration: - -```txt -/Series/Completed/*:60 -/Series/Updating/*/**:10 -/Series/Archived/**:30 -``` +## 自定义缓存策略 -Explanation: - -- `*` matches only a single directory level. Items directly under `/Series/Completed` will be cached for 60 minutes. This does **not** include deeper subdirectories — for example, `/Series/Completed/A/B` will not match this rule. -- `**` matches multiple directory levels. Therefore, the contents of subdirectories under `/Series/Updating` will be cached for 10 minutes. For example, `/Series/Updating/A/B` and `/Series/Updating/C/D` will match. -- The pattern `/Series/Updating/*/**` enforces a “single level followed by multi-level” match. As a result, directories directly under `/Series/Updating` will **not** be matched by this rule. -- All contents under `/Series/Archived` (including any depth of subdirectories) will be cached for 30 minutes. - -::: - -::: zh-CN - -目录路径缓存时间(单位:分钟)。 +目录路径缓存时间(单位:分钟)。 可以通过模式匹配来自定义某些文件路径的缓存时间。配置支持通配符: @@ -240,31 +102,11 @@ Explanation: - `**` 匹配多层目录,因此 `/剧集/更新中` 下级目录的内容会被缓存 10 分钟。例如 `/剧集/更新中/A/B` 和 `/剧集/更新中/C/D` 都会符合该规则。 - 由于 `/剧集/更新中/*/**` 严格设置了模糊匹配单层目录,所以直属于 `/剧集/更新中` 下都目录将不会命中规则。 - `/剧集/归档` 下的内容(包括任意层级的子目录)都会被缓存 30 分钟。 -::: - -## Web proxy { lang="en" } -## Web 代理 { lang="zh-CN" } +## Web 代理 -::: en -Whether the web preview,download and the direct link go through the transfer. If you open this, recommended you set [site_url](../../configuration/configuration.md#site-url) so that OpenList can works fine. -::: -::: zh-CN 网页预览、下载和直接链接是否通过中转。如果你打开此项,建议你设置[site_url](../../configuration/configuration.md#site-url),以帮助OpenList更好的工作。 -::: -::: en -::: tip - -- **Web proxy Strategies:** It is a strategy when using the webpage. The default is a local agent. If you fill in the proxy URL and enable the web agent to use the proxy URL -- **Webdav policy Strategies:** It is an option to use the webdav function - - If there are 302 options default to 302, if there is no 302 option default to the local agent, if you want to use the agent URL, please fill in and manually switch to the proxy URL strategy - -The two are different configurations. - -::: - -::: zh-CN ::: tip - **Web代理**:是使用网页时候的策略,默认为本地代理,如果填写了代理URL并且启用了Web代理使用的是代理URL @@ -275,22 +117,7 @@ The two are different configurations. ::: -## Webdav policy { lang="en" } - -## WebDAV 策略 { lang="zh-CN" } - -::: en - -- **302 redirect:** redirect to the real link - - Although it does not consume traffic, it is not recommended to share and use it. -- **use proxy URL:** redirect to proxy URL - - It will consume the traffic of the agent URL -- **native proxy:** return data directly through local transit(best compatibility) - - The traffic of the construction of OpenList device will consume - -::: - -::: zh-CN +## WebDAV 策略 - **302 重定向**:重定向到真实链接 - 虽然不会消耗流量,但是不建议共享使用,有封禁账户的风险 @@ -299,33 +126,7 @@ The two are different configurations. - **本机代理**:直接通过本地中转返回数据(最佳兼容性) - 会消耗搭建OpenList设备的流量 -::: - -### Description of three modes { lang="en" } - -### 三种模式说明 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: 302 redirect, use proxy URL, native proxy ---- -flowchart LR - E(User operation OpenList calls API
triggers related API operations Cloud Drive
Cloud Drive receives command and begins execution) - A[(Cloud Drive)] - B(You) - C[Use proxy URL] - D{Native proxy} - E==>A==>E - A==>|Directly transferring from cloud drive to you without any loss
302 downloads as fast as possible|B - A-->|Send the cloud disk to the use proxy URL first|C-->|After being forwarded to you by the proxy URL, your download speed depends on the speed of downloading, forwarding, and forwarding on the other end of the proxy URL|B - A-->|Send the cloud drive to the device running OpenList first|D-->|After being forwarded to you by the device running OpenList, your download speed depends on the speed of downloading, uploading, and forwarding on the end where OpenList is built|B -``` - -::: - -::: zh-CN +### 三种模式说明 ```mermaid --- @@ -343,45 +144,12 @@ flowchart LR A-->|云盘先发送给搭建OpenList的设备|D-->|由搭建OpenList的设备转发给你
转发给你之后,你下载速度
取决于搭建OpenList那一端下载然后上传转发的速度|B ``` -::: - -## Download proxy URL { lang="en" } +## 下载代理 URL -## 下载代理 URL { lang="zh-CN" } - -::: en -When the proxy is turned on without filling in this field, the local machine will be used for transfer by default. -::: - -::: zh-CN 开启代理时不填写此字段,默认使用本机进行传输。 -::: - -### 1. Cloudflare Workers { lang="en" } - -### 1. Cloudflare Workers { lang="zh-CN" } - -::: en -Here’s the translation: - -You can use Cloudflare Workers as a proxy. Simply fill in your Cloudflare Workers address here. - -The code to set up Workers can be found at [https://github.com/OpenListTeam/OpenList-Proxy/blob/main/openlist-proxy.js](https://github.com/OpenListTeam/OpenList-Proxy/blob/main/openlist-proxy.js). When using it, you need to replace the following variables: -- `ADDRESS`: Your OpenList address, which must include the protocol header and should not end with a `/`. For example, `https://pan.example.com`. +### 1. Cloudflare Workers -- `TOKEN`: The [Token](../../configuration/other.md#token) of the admin account, which can be found in the “Other Settings” section of the OpenList admin page. - -- `WORKER_ADDRESS`: Your Worker address, which is usually the same as the **Download Proxy URL**. - - :warning: Cloudflare Workers free CDN support is only compatible with **http80** and **https443** ports (whether domestic or international), as tested by group members. - -When filling in the **Download Proxy URL** in the OpenList backend configuration, the link should not end with a `/`. - -Detailed text tutorial: -::: - -::: zh-CN 可以使用 Cloudflare Workers 做代理,这里填写您的 Cloudflare Workers 地址即可。 搭建 Workers 代码可以在 https://github.com/OpenListTeam/OpenList-Proxy/blob/main/openlist-proxy.js 找到,实际使用时需要配置环境变量: @@ -392,50 +160,15 @@ Detailed text tutorial: 来自安稳的详细文字教程: -::: - -### 2. Universal Binary { lang="en" } - -### 2. 通用二进制 { lang="zh-CN" } +### 2. 通用二进制 -::: en -You can use another machine as a proxy. Download the program from https://github.com/OpenListTeam/OpenList-Proxy/releases and check the usage instructions with `./openlist-proxy -help`. - -Detailed text tutorial: -::: - -::: zh-CN 您可以使用另一台机器进行代理,在 https://github.com/OpenListTeam/OpenList-Proxy/releases 下载程序并通过 `./openlist-proxy -help` 查看使用方法。 更多内容请参考[OpenList Proxy](../../ecosystem/official_proxy) 来自安稳的详细文字教程: -::: - -### 3. Developing on your own { lang="en" } -### 3. 自行开发 { lang="zh-CN" } - -::: en -You can develop your own proxy program. The general steps are as follows: - -- When downloading, it will request `PROXY_URL/path?sign=sign_value`. -- In the proxy program, validate the `sign`. The calculation method for `sign` is: - -```js -const to_sign = `${path}:${expireTimeStamp}` -const _sign = safeBase64(hmac_sha256(to_sign, TOKEN)) -const sign = `${_sign}:${expireTimeStamp}` -``` - -`TOKEN` is the [Token](../../configuration/other.md#token) of the administrator account, which can be obtained in the “Other Settings” section of the OpenList management page. - -- After validating the signature, request `HOST/api/fs/link` to obtain the file URL and the request headers to include. -- Use the information to make the request and handle the response. - -::: - -::: zh-CN +### 3. 自行开发 你可以开发自己的代理程序,一般步骤是: @@ -453,48 +186,16 @@ const sign = `${_sign}:${expireTimeStamp}` - 验证签名正确后,请求 `HOST/api/fs/link`,可以得到文件的 URL 和要携带的请求头 - 使用信息请求和返回 -::: - -## Sort related { lang="en" } - -## 排序相关 { lang="zh-CN" } - -::: en - -- **Sort by**: Sort by what -- **Sort direction**: Whether the sort direction is ascending or descending - -::: - -::: zh-CN +## 排序相关 - **排序方式**:按什么排序 - **排序方向**:排序方向是升序还是降序 -::: - -::: en -::: info -Some drives use their own sorting method, which may be different. -::: -::: zh-CN ::: info 有些驱动器使用自己的排序方法,可能会有所不同。 ::: -## Extract folder { lang="en" } - -## 提取文件夹 { lang="zh-CN" } - -::: en - -- **Extract to front**: put all folders to the front when sorting -- **Extract to back**: put all folders to the back when sorting - -::: -::: zh-CN +## 提取文件夹 - **提取到前面**:排序时将所有文件夹放在前面 - **提取到后面**:排序时将所有文件夹放在后面 - -::: diff --git a/pages/guide/drivers/crypt.md b/pages/guide/drivers/crypt.md index 7331e33e1..b69a0e095 100644 --- a/pages/guide/drivers/crypt.md +++ b/pages/guide/drivers/crypt.md @@ -1,32 +1,12 @@ --- -title: - en: Crypt - zh-CN: Crypt(加密) -icon: iconfont icon-state top: 996 categories: - guide - drivers -tag: - - Storage - - Guide -sticky: true -star: true --- -::: en -The Crypt driver provides secure encryption for your files and folders, acting as a two-password-protected vault. Only users with the correct password and salt can access the encrypted content. +# Crypt(加密) -**Key Features:** - -- File and folder encryption with multiple security levels -- Password and salt-based protection -- Compatible with rclone crypt -- Supports various encryption modes for filenames and directories - -::: - -::: zh-CN Crypt 驱动为您的文件和文件夹提供安全加密,相当于一个双重密码保护的保险库。只有拥有正确密码和盐值的用户才能访问加密内容。 **主要特性:** @@ -36,20 +16,6 @@ Crypt 驱动为您的文件和文件夹提供安全加密,相当于一个双 - 与 rclone crypt 完全兼容 - 支持多种文件名和目录加密模式 -::: - -::: en -::: warning Important Security Notice - -1. Read this guide thoroughly before using the Crypt driver -2. Test the configuration in a local environment before production deployment -3. **Never modify the configuration after storing encrypted data** - this will make data lost -4. Keep your password and salt values secure - losing them means losing access to your data - -Again, please read the documentation carefully; otherwise, any data loss will be at your own risk! -::: - -::: zh-CN ::: warning 重要安全提醒 1. 使用 Crypt 驱动前请仔细阅读本指南 @@ -60,36 +26,7 @@ Again, please read the documentation carefully; otherwise, any data loss will be **再次提醒请仔细阅读文档使用,否则数据丢失自行承担!** ::: -## Setup Instructions { lang="en" } - -## 设置说明 { lang="zh-CN" } - -::: en - -1. **Create Storage Location** - - Create an **empty folder** in your existing mounted drive to store encrypted files. - -2. **Configure Remote Path** - - Enter the path of the empty folder in the `Remote path` field of your Crypt driver configuration. - - **Example Configuration:** - - Original driver path: `/123` - - New empty folder: `/123/encrypted_storage` - - Remote path setting: `/123/encrypted_storage` - -3. **Upload Files** - - Upload files to the newly created Crypt driver mount point. Only files uploaded through the Crypt driver will be encrypted. - - **File Access:** - - **Encrypted files**: Located in the remote path, appear scrambled and cannot be opened directly - - **Decrypted access**: View and access files normally through the Crypt driver mount point - -::: - -::: zh-CN +## 设置说明 1. **创建存储位置** @@ -112,45 +49,12 @@ Again, please read the documentation carefully; otherwise, any data loss will be - **加密文件**:位于远程路径中,显示为加密状态,无法直接打开 - **解密访问**:通过 Crypt 驱动挂载点正常查看和访问文件 -::: - -### Basic Configuration Example { lang="en" } - -### 基础配置示例 { lang="zh-CN" } - -::: en -For users new to encryption, use the following default configuration: +### 基础配置示例 -![Crypt Configuration Example](/img/drivers/crypt/crypt-demo1.png) -::: - -::: zh-CN 对于首次使用加密的新手用户,建议使用以下默认配置: ![Crypt 配置示例](/img/drivers/crypt/crypt-demo1.png) -::: - -::: en -::: danger Please make sure to read the following important notes carefully to ensure understanding! - -**Important Declaration:** - -- **Do not modify the configuration! Do not modify the configuration! Do not modify the configuration!** Once the configuration is saved, do not modify it again!!! This is repeated for emphasis! -- **Password** and **Salt** must be remembered! After clicking save, they will be encrypted and cannot be displayed in plain text (the plain text in the image shows the state before saving). - -> [**Password Configuration Instructions**](#password) -> -> [**Salt Configuration Instructions**](#salt) ---- - -- **If you have not yet uploaded files within the Crypt driver**, you can modify the password and configuration. **Otherwise, do not modify!** -- After modifying the configuration, Crypt will attempt to filter illegal files/directories, but illegal data will not be automatically deleted. - - **Illegal files/directories** refer to encrypted data generated with a different configuration. - -::: - -::: zh-CN ::: danger 请务必仔细阅读以下注意事项,确保理解! **重要声明:** @@ -170,22 +74,6 @@ For users new to encryption, use the following default configuration: ::: -::: en -::: warning -Regarding encryption combinations, there are 5 options available (actually 6), but it’s important to note that if only folder encryption is enabled without encrypting the file names, the configuration will not take effect (as shown in the first example below). - -| Filename Encryption | Directory Encryption | Status | -| ------------------- | -------------------- | ---------- | -| `Off` | `Enabled` | ❌ Invalid | -| `Off` | `Disabled` | ✅ Valid | -| `Standard` | `Disabled` | ✅ Valid | -| `Standard` | `Enabled` | ✅ Valid | -| `Obfuscate` | `Disabled` | ✅ Valid | -| `Obfuscate` | `Enabled` | ✅ Valid | - -::: - -::: zh-CN ::: warning 关于加密组合,共有5种方式可供选择(实际上是6种),但需要注意的是,如果只启用文件夹加密而不加密文件名的配置将不会生效(例如下面举例的第1种情况)。 @@ -200,36 +88,10 @@ Regarding encryption combinations, there are 5 options available (actually 6), b ::: -## Configuration Options { lang="en" } - -## 配置选项 { lang="zh-CN" } - -### Filename Encryption { lang="en" } - -### 文件名加密 { lang="zh-CN" } - -::: en -**Default:** `Disabled` - -**Available Options:** - -| Mode | Security Level | Description | -| ----------- | -------------- | ----------------------------------------------------------------------------- | -| `Off` | None | Files keep original names with encrypted suffix (e.g., `file.txt.bin`) | -| `Standard` | High | **Recommended** - Strong encryption with good security | -| `Obfuscate` | Low | Simple obfuscation, supports long filenames but may create special characters | - ---- - -In the image below, the left side shows file **name encryption** and folder **name encryption** enabled, while the right side shows the decrypted Crypt driver where files can be viewed. +## 配置选项 -- **File name encryption not enabled**: Original name + encrypted suffix, as shown in the top left corner -- **File name encryption enabled**: Fully encrypted file name, content cannot be recognized, as shown in the bottom left corner +### 文件名加密 -![Filename Encryption Comparison](/img/drivers/crypt/crypt-demo2.png) -::: - -::: zh-CN **默认值:** `禁用` **可用选项:** @@ -248,31 +110,9 @@ In the image below, the left side shows file **name encryption** and folder **na - **启用文件名加密**:完全加密的文件名,无法识别内容,如左下角 ![文件名加密对比](/img/drivers/crypt/crypt-demo2.png) -::: - -### Directory Name Encryption { lang="en" } - -### 文件夹名称加密 { lang="zh-CN" } - -::: en -**Default:** `Disabled` - -Directory encryption requires filename encryption to be enabled. When activated, folder names are also encrypted for enhanced security. -**Configuration Combinations:** +### 文件夹名称加密 -| Filename Encryption | Directory Encryption | Status | -| ------------------- | -------------------- | ---------- | -| `Off` | `Enabled` | ❌ Invalid | -| `Off` | `Disabled` | ✅ Valid | -| `Standard` | `Disabled` | ✅ Valid | -| `Standard` | `Enabled` | ✅ Valid | -| `Obfuscate` | `Disabled` | ✅ Valid | -| `Obfuscate` | `Enabled` | ✅ Valid | - -::: - -::: zh-CN **默认值:** `禁用` 目录加密需要先启用文件名加密。激活后,文件夹名称也会被加密以增强安全性。 @@ -288,142 +128,56 @@ Directory encryption requires filename encryption to be enabled. When activated, | `Obfuscate(混淆)` | `禁用` | ✅ 有效 | | `Obfuscate(混淆)` | `启用` | ✅ 有效 | -::: - -### Remote Path { lang="en" } - -### 加密后文件存储路径 { lang="zh-CN" } +### 加密后文件存储路径 -::: en -The storage location for encrypted files. Can be any mountable drive supported by OpenList. -::: -::: zh-CN 加密文件的存储位置。可以是 OpenList 支持的任何可挂载驱动。 -::: -### Security Parameters { lang="en" } +### 安全参数 -### 安全参数 { lang="zh-CN" } - -::: en -::: danger -After saving the configuration, both password and salt values are encrypted and cannot be displayed in plain text. Store them securely in a separate location. -::: -::: zh-CN ::: danger 保存配置后,密码和盐值都会被加密,无法以明文显示。请在其他地方安全存储这些值。 ::: -#### Password { lang="en" } +#### 密码 -#### 密码 { lang="zh-CN" } - -::: en -::: warning -Note that passwords exported using `rclone config` are obfuscated and cannot be used directly. Please prefix the password with `___Obfuscated___` before use.
-For example, if the exported password is `abc123xyz`, it should be entered as `___Obfuscated___abc123xyz` in the Crypt driver. -::: - -::: zh-CN ::: warning 请注意,使用rclone config导出的配置中,密码是混淆后的,无法直接使用,请在密码的前缀中添加 `___Obfuscated___` 后再使用。
例如导出的密码是 `abc123xyz`,则在 Crypt 驱动中应填写 `___Obfuscated___abc123xyz`。 ::: -::: en -Primary encryption key. **Must be remembered** - cannot be recovered if lost. -::: -::: zh-CN 主要加密密钥。**必须牢记** - 丢失后无法恢复。 -::: -#### Salt { lang="en" } +#### 盐值 -#### 盐值 { lang="zh-CN" } - -::: en -::: warning -Note that salts exported using `rclone config` are obfuscated and cannot be used directly. Please prefix the salt with `___Obfuscated___` before use.
-For example, if the exported salt is `abc123xyz`, it should be entered as `___Obfuscated___abc123xyz` in the Crypt driver. -::: - -::: zh-CN ::: warning 请注意,rclone config 导出的配置中,盐值(即password2)是混淆后的,无法直接使用,请在盐值的前缀中添加 `___Obfuscated___` 后再使用。
例如导出的盐值是 `abc123xyz`,则在 Crypt 驱动中应填写 `___Obfuscated___abc123xyz`。 ::: -::: en -Secondary encryption key, acts as an additional password layer. **Must be remembered** - cannot be recovered if lost. - -If you don't know what is salt, treat it as a second password. Optional but recommended - -::: -::: zh-CN 辅助加密密钥,相当于第二层密码保护。**必须牢记** - 丢失后无法恢复。 如果您不知道什么是加盐,可以视为第二个密码。可选,推荐。 -::: - -### Advanced Options { lang="en" } -### 高级选项 { lang="zh-CN" } +### 高级选项 -#### Encrypted Suffix { lang="en" } +#### 加密后缀 -#### 加密后缀 { lang="zh-CN" } - -::: en -**Default:** `.bin` - -Custom suffix for encrypted files (only used when filename encryption is disabled). Must start with a dot (e.g., `.abc`, `.encrypted`). -::: - -::: zh-CN **默认值:** `.bin` 加密文件的自定义后缀(仅在文件名加密禁用时使用)。必须以点开头(例如:`.abc`、`.encrypted`)。 -::: -#### Filename Encoding { lang="en" } +#### 文件名编码 -#### 文件名编码 { lang="zh-CN" } - -::: en -**Default:** `base64` - -**Warning:** Only modify if you understand the implications. Other encoding options are not thoroughly tested and may cause compatibility issues. - -For rclone compatibility, configure this setting in advanced options. -::: - -::: zh-CN **默认值:** `base64` **警告:** 仅在了解其影响时才修改此设置。其他编码选项未经充分测试,可能导致兼容性问题。 为了与 rclone 兼容,请在高级选项中配置此设置。 -::: - -## Advanced Usage { lang="en" } - -## 高级用法 { lang="zh-CN" } -### Rclone Compatibility { lang="en" } +## 高级用法 -### Rclone 兼容性 { lang="zh-CN" } +### Rclone 兼容性 -::: en -The Crypt driver is fully compatible with [rclone crypt](https://rclone.org/crypt). - -**Important Notes:** - -- OpenList Crypt uses `filename_encoding = base64` by default for better long filename support, configure this setting in advanced options when using with rclone -- Case-insensitive filesystems (e.g., Windows with local storage) may cause issues - -::: - -::: zh-CN Crypt 驱动与 [rclone crypt](https://rclone.org/crypt) 目前完全兼容。 **重要说明:** @@ -431,43 +185,16 @@ Crypt 驱动与 [rclone crypt](https://rclone.org/crypt) 目前完全兼容。 - OpenList Crypt 默认使用 `filename_encoding = base64` 以更好地支持长文件名,与 rclone 一起使用时请在高级选项中配置此设置 - 大小写不敏感的文件系统(如 Windows 本地存储)可能会出现问题 -::: - -## Troubleshooting { lang="en" } - -## 故障排除 { lang="zh-CN" } - -### Startup Errors { lang="en" } +## 故障排除 -### 启动错误 { lang="zh-CN" } +### 启动错误 -::: en -If Crypt shows errors during OpenList startup, it's likely because Crypt starts before its target path is available. - -**Solution:** Set a higher [order number](./common.md#order) for the Crypt driver to delay its initialization. -::: - -::: zh-CN 如果 Crypt 在 OpenList 启动时显示错误,可能是因为 Crypt 在其目标路径可用之前就启动了。 **解决方案:** 为 Crypt 驱动设置更高的[序号](./common.md#序号)以延迟其初始化。 -::: -### Data Access Issues { lang="en" } +### 数据访问问题 -### 数据访问问题 { lang="zh-CN" } - -::: en -If you cannot access previously encrypted data: - -1. Verify that the password and salt values are correct -2. Ensure the configuration hasn't been modified -3. Check that the remote path is accessible -4. Confirm the filename encoding setting matches your original configuration - -::: - -::: zh-CN 如果无法访问之前加密的数据: 1. 验证密码和盐值是否正确 @@ -475,33 +202,7 @@ If you cannot access previously encrypted data: 3. 检查远程路径是否可访问 4. 确认文件名编码设置与原始配置匹配 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -519,5 +220,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/degoo.md b/pages/guide/drivers/degoo.md index 3bce42b64..4b03b83c3 100644 --- a/pages/guide/drivers/degoo.md +++ b/pages/guide/drivers/degoo.md @@ -1,25 +1,14 @@ --- -title: - en: Degoo - zh-CN: Degoo categories: - guide - drivers top: 587 --- -https://degoo.com/ - -::: en -**Authentication methods**: +# Degoo -1. Username + Password -2. Refresh Token -3. Access Token +https://degoo.com/ -In normal cases, you can log in with your username + password. If you encounter a 429 error, you can log in using tokens. You can obtain the token from the request body or header in your browser. -::: -::: zh-CN **鉴权方式**: 1. 用户名 + 密码 @@ -27,79 +16,24 @@ In normal cases, you can log in with your username + password. If you encounter 3. 访问令牌 正常情况下,可以使用用户名 + 密码进行登录。如果您遇到了429错误,可以使用令牌登录。您可以从浏览器的请求内容或头部中获取访问令牌。 -::: -## Username { lang="en" } +## 用户名 -## 用户名 { lang="zh-CN" } - -::: en -Your user's name. -::: -::: zh-CN 您的用户名。 -::: - -## Password { lang="en" } -## 密码 { lang="zh-CN" } +## 密码 -::: en -Your user's password. -::: -::: zh-CN 您用户的密码。 -::: - -## Refresh Token { lang="en" } -## 刷新令牌 { lang="zh-CN" } +## 刷新令牌 -::: en -Refresh token for automatic token renewal, obtained automatically. -::: -::: zh-CN 用于自动更新令牌的刷新令牌,自动获取。 -::: -## Access Token { lang="en" } +## 访问令牌 -## 访问令牌 { lang="zh-CN" } - -::: en -Access token for Degoo API, obtained automatically. -::: -::: zh-CN Degoo API 访问令牌,自动获取。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -119,5 +53,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/develop.md b/pages/guide/drivers/develop.md index 9d920a5ff..0edceb634 100644 --- a/pages/guide/drivers/develop.md +++ b/pages/guide/drivers/develop.md @@ -1,30 +1,11 @@ --- -title: - en: Contribute new storage driver - zh-CN: 贡献新存储驱动 -icon: iconfont icon-code -# This control sidebar order top: 100 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - Develop -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -Duplicate `drivers/template` to expand the new storage. Please read [CONTRIBUTING](https://github.com/OpenListTeam/OpenList/blob/main/CONTRIBUTING.md) for details. -The design of the driver part of OpenList is easy to expand, no need to modify other files, just create a new package under the back-end project drivers package and reference this package in the `all.go` file. -::: -::: zh-CN +# 贡献新存储驱动 + 复制 `drivers/template` 以扩展新存储。 请阅读 [CONTRIBUTING](https://github.com/OpenListTeam/OpenList/blob/main/CONTRIBUTING.md) 了解详情。 OpenList 的驱动部分的设计很容易扩展,不需要修改其他文件,只需在后端项目驱动包下新建一个包,在 `all.go` 文件中引用这个包即可。 -::: diff --git a/pages/guide/drivers/doubao.md b/pages/guide/drivers/doubao.md index 1d0e67431..f8fc67d4b 100644 --- a/pages/guide/drivers/doubao.md +++ b/pages/guide/drivers/doubao.md @@ -1,36 +1,16 @@ --- -title: - en: Doubao Drive - zh-CN: 豆包云盘 -icon: iconfont icon-state -# This control sidebar order top: 676 -# A page can have multiple categories categories: - guide - drivers --- - +# 豆包云盘 -::: en -Mount [Doubao Drive](https://www.doubao.com/chat/drive/) cloud storage. + -::: -::: zh-CN 挂载 [豆包云盘](https://www.doubao.com/chat/drive/) 云存储。 -::: - -::: en -::: danger Security Risk - -Do not use 302 if the storage is public accessible. -Otherwise, the download link may leak sensitive information such as access token or signature. -Others may use the leaked link to **access all your files**. - -::: -::: zh-CN ::: danger 安全风险 若存储可公开访问,则请勿使用302重定向。 @@ -39,41 +19,14 @@ Others may use the leaked link to **access all your files**. ::: -## Root folder ID { lang="en" } - -## 根文件夹ID { lang="zh-CN" } +## 根文件夹ID -::: en -The root folder ID is the parameter value displayed in the address bar when accessing Doubao Drive via a web page. For example, after accessing [Doubao Drive](https://www.doubao.com/chat/drive/), the address bar shows `https://www.doubao.com/chat/drive/AAAAAAAAAAAAAAAAAAAAAAAAAA?tab=myUpload`, where `AAAAAAAAAAAAAAAAAAAAAAAAAA` is the root folder ID. - -![Doubao Drive Root Folder ID](/img/drivers/doubao/doubao-root-folder-id.png) -::: - -::: zh-CN 根文件夹ID即为网页访问豆包云盘时地址栏中的参数值,例如访问[豆包云盘](https://www.doubao.com/chat/drive/)后,地址栏显示为`https://www.doubao.com/chat/drive/AAAAAAAAAAAAAAAAAAAAAAAAAA?tab=myUpload`,其中`AAAAAAAAAAAAAAAAAAAAAAAAAA`即为根文件夹ID。 ![豆包云盘根文件夹ID](/img/drivers/doubao/doubao-root-folder-id.png) -::: - -## Cookie { lang="zh-CN" } - -## Cookie { lang="en" } - -::: en -Required. Web cookie. Used to refresh token and extract Authorization/DPoP tokens. -Open browser developer tools, visit [Doubao Drive](https://www.doubao.com/drive/), log in to your account, check the `Network` tab, search for `biz_auth` and open it, find `Cookie` in the `Header`, copy the complete value and fill it in. - -![Doubao Drive Cookie](/img/drivers/doubao/doubao-cookie.png) - -OpenList will automatically extract other attributes from the Cookie. Please ensure that the Cookie contains the following key-value pairs: - -- `LARK_SUITE_DPOP` -- `LARK_SUITE_ACCESS_TOKEN` -- `feishu_dpop_keypair` (used for key generation, optional) +## Cookie -::: -::: zh-CN 必填,网页 Cookie。用于刷新 Token 和提取 Authorization/DPoP Token。 打开浏览器开发者工具,访问 [豆包云盘](https://www.doubao.com/drive/),登录账号后,查看`Network`(网络)Tab,搜索 `biz_auth` 并打开它,在 `Header`(标头)中找到 `Cookie`,复制其完整值填入即可。 @@ -86,56 +39,20 @@ OpenList 会自动从 Cookie 中提取其他属性。请确保 Cookie 中包含 - `LARK_SUITE_ACCESS_TOKEN` - `feishu_dpop_keypair` (用于密钥生成,可选) -::: - -## App ID { lang="en" } - -## App ID { lang="zh-CN" } - -::: en -必填,Doubao's App ID. The App ID may vary for different clients, please refer to the actual value. -::: +## App ID -::: zh-CN 必填,豆包的App ID。不同客户端的App ID可能不相同,请以实际为准。 -::: - -## DPoP Key Secret, Auth Client ID, Auth Scope, Auth SDK Source, Auth SDK Version { lang="en" } -## DPoP 密钥、认证客户端ID、认证区域、认证SDK来源、认证SDK版本 { lang="zh-CN" } - -::: en -Optional, for Token refresh. Please obtain it yourself, and refer to the actual value. -::: +## DPoP 密钥、认证客户端ID、认证区域、认证SDK来源、认证SDK版本 -::: zh-CN 可选,用于刷新 Token。请自行获取,以实际为准。 -::: - -## Share Link { lang="en" } - -## 分享链接 { lang="zh-CN" } -::: en -Whether to use share link for download. If enabled, OpenList will create share and request with anonymous user's token to create download link. This can reduce authorization leak. - -Please ensure you have filled all authentication options above before enabling this function. -::: +## 分享链接 -::: zh-CN 是否使用分享链接下载。启用后,OpenList 将创建分享然后使用匿名用户Token请求创建下载链接以减少授权泄露。 请确保您在启用此功能前已经填写了上面的所有认证参数。 -::: - -## Ignore JWT Check { lang="en" } -## 忽略 JWT 检查 { lang="zh-CN" } +## 忽略 JWT 检查 -::: en -Whether to ignore JWT check to prevent time issue -::: - -::: zh-CN 是否忽略 JWT 检查以避免时间问题。 -::: diff --git a/pages/guide/drivers/dropbox.md b/pages/guide/drivers/dropbox.md index dca3db801..7b8adec66 100644 --- a/pages/guide/drivers/dropbox.md +++ b/pages/guide/drivers/dropbox.md @@ -1,77 +1,15 @@ --- -title: - en: Dropbox - zh-CN: Dropbox -icon: iconfont icon-state -# This control sidebar order top: 590 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -Dropbox Official:https://www.dropbox.com/ -::: -::: zh-CN -Dropbox官网:https://www.dropbox.com/ -::: - -## Get refresh token { lang="en" } - -## 获取刷新令牌 { lang="zh-CN" } - -::: en - -- **This drive does not support the online API mode provided by OpenList.** - -- The following tutorial applies when the option is kept disabled as shown below. - - ![Disable online_api](/img/drivers/dropbox/7.png) - -- If you create your own `Client ID` and `Secret`, remember to authorize them. - -The steps are as follows: - -1. First, [click here](https://www.dropbox.com/developers/apps?_tk=pilot_lp&_ad=topbar4&_camp=myapps) to enter the Dropbox app management page and click "Create App". - - ![Enter the page](/img/drivers/dropbox/1.png) - -2. After entering the app, configure the app type as shown below. - - ![App type](/img/drivers/dropbox/2.png) - -3. You can get the id and secret in the red box: the upper one is the id, the lower one is the secret. - - ![Parameter location](/img/drivers/dropbox/6.png) - -4. Configure the callback URL. If you are strict about permissions and do not want to use an external callback address, you can set a local address here, or use the one outside the red box. - - ![Callback URL](/img/drivers/dropbox/3.png) - -5. Finally, go to the permissions configuration page to set the app's permissions. - - ![Permission configuration](/img/drivers/dropbox/4.png) - -6. [Click here](https://api.oplist.org/) to enter the token acquisition tool. Select Dropbox, fill in your id and secret, and after authorization you can get the refresh token. - -7. In the OpenList configuration page, enter the refresh token, id, and secret to use. Note that the refresh token is about 40-50 characters long. +# Dropbox -![OpenList configuration](/img/drivers/dropbox/5.png) - -::: +Dropbox官网:https://www.dropbox.com/ -::: zh-CN +## 获取刷新令牌 - **该驱动并不支持由 OpenList 提供的 online api 模式!** @@ -109,18 +47,6 @@ The steps are as follows: ![openlist配置](/img/drivers/dropbox/5.png) -::: - -::: en -If you are highly privacy-conscious, Dropbox supports local callback. You can use the following script provided by GPT to quickly implement it, communicating only with Dropbox servers. - -::: warning -Since the callback address is local and you have not set up a real local callback server, please manually copy the authorization code from the browser address bar. - -**Please resolve Python environment issues yourself, or use the callback server provided above.** -::: - -::: zh-CN 如果你有强烈的隐私意识,dropbox支持本地回调,可以使用以下全程由GPT提供的脚本快速实现,只和dropbox的服务器进行通信。 ::: warning @@ -129,48 +55,6 @@ Since the callback address is local and you have not set up a real local callbac **请自行解决py运行的环境问题,或者使用上面搭建好的回调服务器** ::: -::: en - -```python -import requests -import webbrowser -# Please replace with your own Dropbox App information -CLIENT_ID = 'your_app_key' -CLIENT_SECRET = 'your_app_secret' -REDIRECT_URI = 'http://localhost:114514' -# Step 1: Get authorization code -auth_url = ( - f"https://www.dropbox.com/oauth2/authorize" - f"?client_id={CLIENT_ID}" - f"&redirect_uri={REDIRECT_URI}" - f"&response_type=code" - f"&token_access_type=offline" # Required: key parameter to get refresh_token -) -print("👉 Please visit the following link to authorize:\n") -print(auth_url) -webbrowser.open(auth_url) -auth_code = input("\n✅ After authorization, paste the code after ?code= in the redirected URL here:\n> ").strip() -# Step 2: Exchange for access_token + refresh_token -token_url = "https://api.dropboxapi.com/oauth2/token" -data = { - 'code': auth_code, - 'grant_type': 'authorization_code', - 'client_id': CLIENT_ID, - 'client_secret': CLIENT_SECRET, - 'redirect_uri': REDIRECT_URI -} -response = requests.post(token_url, data=data) -response.raise_for_status() -tokens = response.json() -# ✅ Only output the refresh token -print("\n🎉 Success! Your Dropbox refresh_token is:\n") -print(tokens.get("refresh_token")) -``` - -::: - -::: zh-CN - ```python import requests import webbrowser @@ -207,50 +91,13 @@ print("\n🎉 获取成功!你的 Dropbox refresh_token 是:\n") print(tokens.get("refresh_token")) ``` -::: - -## Root folder file_id { lang="en" } +## 根文件夹ID -## 根文件夹ID { lang="zh-CN" } - -::: en -**Empty is the root directory**:display all files -**Single folder ID**:Enter the folder you need to display, copy the top link to fill in `/home` behind -![](/img/drivers/dropbox/folder_id.png) -::: -::: zh-CN 空为根目录:挂载全部文件 单文件夹ID:进入你需要挂载的文件夹复制顶部链接将`/home`后面的填写进去即可 ![](/img/drivers/dropbox/folder_id.png) -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -270,5 +117,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/emby.md b/pages/guide/drivers/emby.md index 684ea3477..f29071a3d 100644 --- a/pages/guide/drivers/emby.md +++ b/pages/guide/drivers/emby.md @@ -1,136 +1,54 @@ --- -title: - en: Emby - zh-CN: Emby -icon: iconfont icon-state -# This control sidebar order top: 493 -# A page can have multiple categories categories: - guide - drivers --- -::: en -Mount media resources from an Emby server. -::: -::: zh-CN -挂载 Emby 服务器中的媒体资源。 -::: +# Emby -## URL { lang="en" } +挂载 Emby 服务器中的媒体资源。 -## URL 服务器地址 { lang="zh-CN" } +## URL 服务器地址 -::: en -Emby server address, for example: `http://127.0.0.1:8086`. -::: -::: zh-CN Emby 服务器地址,例如:`http://127.0.0.1:8086`。 -::: - -## ApiKey { lang="en" } -## ApiKey { lang="zh-CN" } +## ApiKey -::: en -Emby API Key. Use together with `UserID` for API authentication. -::: -::: zh-CN Emby API Key。与 `UserID` 搭配使用进行 API 认证。 -::: -## UserID { lang="en" } +## UserID -## UserID { lang="zh-CN" } - -::: en -Emby user ID. Used with `ApiKey`. -::: -::: zh-CN Emby 用户 ID。与 `ApiKey` 搭配使用。 -::: - -## Username { lang="en" } -## 用户名 { lang="zh-CN" } +## 用户名 -::: en -Emby account username. Used with `Password`. -::: -::: zh-CN Emby 账号用户名。与 `Password` 搭配使用。 -::: -## Password { lang="en" } +## 密码 -## 密码 { lang="zh-CN" } - -::: en -Emby account password. Used with `Username`. -::: -::: zh-CN Emby 账号密码。与 `Username` 搭配使用。 -::: - -## LinkMethod { lang="en" } - -## 链接方式 LinkMethod { lang="zh-CN" } -::: en -Choose link method: +## 链接方式 LinkMethod -- `stream`: Streaming link (recommended) -- `download`: Direct download link - -Some Emby servers do not grant download permission. In that case, please use `stream`. -::: -::: zh-CN 选择链接方式: - `stream`:串流链接(推荐) - `download`:下载链接 有些 Emby 服务器未开放下载权限,此时请使用 `stream`。 -::: - -## Authentication Method { lang="en" } -## 认证方式 { lang="zh-CN" } +## 认证方式 -::: en -Use **one** of the following login methods: - -1. `ApiKey + UserID` -2. `Username + Password` - -Do not mix both methods at the same time. -::: -::: zh-CN 登录方式二选一: 1. `ApiKey + UserID` 2. `Username + Password` 请不要同时混用两种方式。 -::: - -## Example Configuration { lang="en" } -## 配置示例 { lang="zh-CN" } - -::: en - -- URL: `http://127.0.0.1:8086` -- LinkMethod: `stream` -- Authentication: choose one method only - -::: - -::: zh-CN +## 配置示例 - URL:`http://127.0.0.1:8086` - LinkMethod:`stream` - 认证方式:仅选择其中一种 - -::: diff --git a/pages/guide/drivers/febbox.md b/pages/guide/drivers/febbox.md index f1b4994a5..8a8affa4a 100644 --- a/pages/guide/drivers/febbox.md +++ b/pages/guide/drivers/febbox.md @@ -1,54 +1,19 @@ --- -title: - en: FebBox - zh-CN: FebBox -icon: iconfont icon-state -# This control sidebar order top: 591 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -FebBox:https://www.febbox.com - -- The upload function is currently unavailable. +# FebBox -::: -::: zh-CN FebBox:https://www.febbox.com - 需要代理,直连似乎无法访问? - 目前上传功能不可用 -::: - -## Root folder ID { lang="en" } - -## 根文件夹ID { lang="zh-CN" } - -::: en -Root directory ID, default is `0`. +## 根文件夹ID -Other directory IDs can be viewed in the top link address bar after entering the folder. - -- **https://www.febbox.com/console#/files?parent_id=66889900** - - Then the directory ID is `66889900` - -::: -::: zh-CN 根目录ID,默认为`0`。 其它目录 ID 查看进入文件夹后看顶部链接地址栏。 @@ -57,74 +22,21 @@ Other directory IDs can be viewed in the top link address bar after entering the 那这个目录 ID 就是 `66889900` -::: - -## `Client_id`、`Client_secret` { lang="en" } +## 客户端 ID 和 秘钥 -## 客户端 ID 和 秘钥 { lang="zh-CN" } - -::: en -Generate address:**https://www.febbox.com/open/clients** - -- The generated client ID and secret key are filled in in the opposite order to the OpenList, so be careful not to fill them in incorrectly. - - ![](/img/drivers/febbox/febox.png) - -::: -::: zh-CN 生成地址:**https://www.febbox.com/open/clients** - 生成的客户端 ID 和秘钥和 OpenList 填写的顺序是相反的,注意别填错 ![](/img/drivers/febbox/febox.png) -::: - -## User IP { lang="en" } - -## 用户 IP { lang="zh-CN" } - -::: en -**Optional**, the IP address of the user when downloading, quoting the official description - -> IP address, Optional parameter. Supports IPv6 format. After filling in, the best download server suitable for the IP location will be selected. If not filled in, the requested IP will be used. +## 用户 IP -::: - -::: zh-CN **可选** ,用户下载时的 IP,引用官方说明 > IP 地址,可选参数。支持 IPv6 格式。填写后,将选择适合 IP 位置的最佳下载服务器。如果未填写,将使用请求的 IP。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -144,5 +56,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/feiji.md b/pages/guide/drivers/feiji.md index 317bd54df..646fdc622 100644 --- a/pages/guide/drivers/feiji.md +++ b/pages/guide/drivers/feiji.md @@ -1,101 +1,34 @@ --- -title: - en: FeiJi Cloud - zh-CN: 小飞机网盘 -icon: iconfont icon-state -# This control sidebar order top: 686 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 小飞机网盘 + **https://feijipan.com/** -::: en -::: danger -Feiji Cloud imposes login restrictions on 3rd-party platforms. Attempting to access its service via OpenList may result in your account being banned. Please proceed with caution! -::: - -::: zh-CN ::: danger 小飞机网盘对第三方平台有登录限制,尝试登录极易封号,请谨慎使用! ::: -## Root folder ID { lang="en" } +## 根文件夹ID -## 根文件夹ID { lang="zh-CN" } - -::: en -root folder ID the default is `0`,Other directory ID View the figure below obtaining method -![](/img/drivers/feiji/feiji.png) -::: -::: zh-CN 根目录ID,默认为`0`,其它目录ID查看下图获取方式 ![](/img/drivers/feiji/feiji.png) -::: -## username、password { lang="en" } +## 账户、密码 -## 账户、密码 { lang="zh-CN" } - -::: en -Just fill in your own Feiji Cloud Account Password -::: -::: zh-CN 填写自己的小飞机网盘帐号密码 -::: - -## Known issues { lang="en" } -## 已知问题 { lang="zh-CN" } +## 已知问题 -::: en -The file size returned by iLanZou is in Kilo Bytes, not Bytes. Therefore, you cannot accurately determine if a file has been modified based on its size. Please pay attention to the configuration of your sync software. -::: -::: zh-CN 蓝奏云优享版返回的文件大小非 Bytes,而是 Kilo Bytes,因此无法使用文件大小准确判断一个文件是否被修改,需要注意同步软件的配置。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -115,5 +48,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/ftp.md b/pages/guide/drivers/ftp.md index 3a12fb35f..f511964b9 100644 --- a/pages/guide/drivers/ftp.md +++ b/pages/guide/drivers/ftp.md @@ -1,106 +1,33 @@ --- -title: - en: FTP - zh-CN: FTP -icon: iconfont icon-state -# This control sidebar order top: 898 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -### Address { lang="en" } +# FTP -## 地址 { lang="zh-CN" } +## 地址 -::: en -FTP address, need contains port. -::: -::: zh-CN FTP 地址,需要包含端口。 -::: -### Username { lang="en" } +## 用户名 -## 用户名 { lang="zh-CN" } - -::: en -FTP username -::: -::: zh-CN FTP 用户名 -::: - -### Password { lang="en" } -## 密码 { lang="zh-CN" } +## 密码 -::: en -FTP password -::: -::: zh-CN FTP 密码 -::: - -### Root folder path { lang="en" } -## 根文件夹ID { lang="zh-CN" } +## 根文件夹ID -::: en -root folder , default `/`, same as local storage. -::: -::: zh-CN 根文件夹,默认 `/`,同本地存储。 -::: -## Enter directory before listing { lang="en" } +## 列出前先进入目录 -## 列出前先进入目录 { lang="zh-CN" } - -::: en -Whether to change into the target directory before listing files. Some FTP servers do not support listing files with a path argument directly. Enable this option to first `cd` into the directory and then `ls`, which can resolve such compatibility issues. Default: `false`. -::: - -::: zh-CN 是否在列出文件前先进入目标目录。部分 FTP 服务器不支持直接带路径参数列出文件,开启此选项后会先 `cd` 进入目录再 `ls`,可以解决此类兼容性问题。默认: `false`。 -::: - -### The default download method used { lang="en" } -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -118,5 +45,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/github.md b/pages/guide/drivers/github.md index 99a78fedf..a2a38ac38 100644 --- a/pages/guide/drivers/github.md +++ b/pages/guide/drivers/github.md @@ -1,36 +1,12 @@ --- -title: - en: GitHub API - zh-CN: GitHub API -icon: iconfont icon-state -# This control sidebar order top: 399 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' - - '官方' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -::: tip -GitHub API does not support operating files larger than 100MiB and folders containing more than 100,000 sub-items. - -All `.gitkeep` files will be managed by the driver and will be invisible to the user. - -Operations on submodules are currently not supported. -::: +# GitHub API -::: zh-CN ::: tip GitHub API 不支持操作 100M 或更大的文件,不支持操作含有超过 100,000 个子文件(夹)的文件夹。 @@ -39,28 +15,7 @@ GitHub API 不支持操作 100M 或更大的文件,不支持操作含有超过 本驱动暂不支持操作子模块。 ::: -## Token { lang="en" } - -## Token { lang="zh-CN" } - -::: en - -1. Log in GitHub and visit . - -2. Select `Generate new token (Classic)` - - ![](/img/drivers/github/1.png) - -3. Fill the information. At least, select **repo**. And click `Generate token`. - - ![](/img/drivers/github/2.png) - -4. Copy the token and paste it into the `Token` field. - - ![](/img/drivers/github/3.png) - -::: -::: zh-CN +## Token 1. 登录 GitHub,访问: @@ -76,58 +31,24 @@ GitHub API 不支持操作 100M 或更大的文件,不支持操作含有超过 ![](/img/drivers/github/3.png) -::: - -## Owner { lang="en" } - -## 所有者 { lang="zh-CN" } +## 所有者 -::: en -The owner of the repository. For example, if the URL of the repository is `github.com/AAA/BBB`, fill in `AAA` here. -::: -::: zh-CN 仓库的所有者,比如仓库链接是`github.com/AAA/BBB`,这里就填`AAA`。 -::: - -## Repo { lang="en" } -## 仓库 { lang="zh-CN" } +## 仓库 -::: en -The name of the repository. For example, if the URL of the repository is `github.com/AAA/BBB`, fill in `BBB` here. -::: -::: zh-CN 仓库名,比如仓库链接是`github.com/AAA/BBB`,这里就填`BBB`。 -::: - -## Ref { lang="en" } -## 引用 { lang="zh-CN" } +## 引用 -::: en -A branch, a tag or a commit SHA. Only when a branch name is filled in is the driver writable. It defaults to the default branch if not filled. -::: -::: zh-CN 分支名、tag或提交SHA,只有在填写分支名的情况下驱动才可写,不填会使用默认分支。 -::: - -## Gh Proxy { lang="en" } - -## Gh Proxy { lang="zh-CN" } - -::: en -Used to speed up downloading -- Download the content of `Raw` and `archive` and change it to `https://gh-proxy.com/raw.githubusercontent.com` +## Gh Proxy -::: -::: zh-CN 用于加速下载 - 下载 `Raw`、`archive` 的内容要改成 `https://gh-proxy.com/raw.githubusercontent.com` -::: - ``` https://gh-proxy.com/raw.githubusercontent.com ``` @@ -140,104 +61,38 @@ https://ghfast.top/raw.githubusercontent.com https://ghproxy.net/raw.githubusercontent.com ``` -## GPG private key { lang="en" } - -## GPG 私钥 { lang="zh-CN" } - -::: en -GPG private key, used to provide commit verifications. Verified commits will be marked with a green "Verified" label on the GitHub webpage. Use the following command to obtain it. Leave blank if verifications are not needed. +## GPG 私钥 -```bash -gpg --export-secret-key -a -``` - -::: -::: zh-CN GPG 私钥,用于提供提交验证功能,经验证的提交会在 GitHub 网页上被标记为绿色的“Verified”,使用以下命令获取,不需要验证功能可以留空。 ```bash gpg --export-secret-key -a <密钥对ID> ``` -::: +## GPG 私钥密码 -## GPG key passphrase { lang="en" } - -## GPG 私钥密码 { lang="zh-CN" } - -::: en -Passphrase to the GPG private key. Leave blank if there is none. -::: -::: zh-CN GPG 私钥的密码,没有则留空。 -::: - -## Committer name { lang="en" } -## 提交者名称 { lang="zh-CN" } +## 提交者名称 -::: en -Custom committer name, defaulting to the owner's of the token when not filled. -::: -::: zh-CN 自定义提交者名称,不填会使用 Token 的所有者。 -::: - -## Committer email { lang="en" } -## 提交者邮箱 { lang="zh-CN" } +## 提交者邮箱 -::: en -Custom committer name, defaulting to the owner's of the token when not filled. -Required when "Committer name" is filled. Required to be empty when "Committer name" is empty. -::: -::: zh-CN 自定义提交者邮箱,不填会使用 Token 的所有者。 填写“提交者名称”时必填,未填写“提交者名称”时必不填。 -::: - -## Author name { lang="en" } -## 作者名称 { lang="zh-CN" } +## 作者名称 -::: en -Custom author name, defaulting to the owner's of the token when not filled. -::: -::: zh-CN 自定义作者名称,不填会使用 Token 的所有者。 -::: - -## Author email { lang="en" } -## 作者邮箱 { lang="zh-CN" } +## 作者邮箱 -::: en -Custom author name, defaulting to the owner's of the token when not filled. -Required when "Author name" is filled. Required to be empty when "Author name" is empty. -::: -::: zh-CN 自定义作者邮箱,不填会使用 Token 的所有者。 填写“作者名称”时必填,未填写“作者名称”时必不填。 -::: -## commit message { lang="en" } - -## 提交信息 { lang="zh-CN" } - -::: en -Custom commit message, supporting the following template variables: - -- `UserName`: the user operating the file. -- `ObjName`: the name of the file (or the folder) operated. -- `ObjPath`: the path to the file (or the folder) operated. -- `ParentName`: the name of the parent folder of the file (or the folder) operated. -- `ParentPath`: the path to the parent folder of the file (or the folder) operated. -- `TargetName`: (when renaming) new name. (when copying and moving) the target folder name. -- `TargetPath`: (when renaming) new path. (when copying and moving) the target path. - -::: +## 提交信息 -::: zh-CN 自定义提交信息,支持以下模板变量: - `UserName`: 发起操作的用户 @@ -248,35 +103,7 @@ Custom commit message, supporting the following template variables: - `TargetName`: (重命名时)新名称(复制、移动时)目标文件夹名称。 - `TargetPath`: (重命名时)新路径(复制、移动时)目标路径 -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -296,5 +123,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/github_releases.md b/pages/guide/drivers/github_releases.md index 4a786a189..dd7ed25f5 100644 --- a/pages/guide/drivers/github_releases.md +++ b/pages/guide/drivers/github_releases.md @@ -1,47 +1,18 @@ --- -title: - en: GitHub Releases - zh-CN: GitHub 发行版 -icon: iconfont icon-state top: 398 categories: - guide - drivers -tag: - - Storage - - Guide - - '302' - - '官方' -sticky: true -star: true --- -## Overview { lang="en" } +# GitHub 发行版 -## 概述 { lang="zh-CN" } +## 概述 -::: en -GitHub Releases driver allows you to mount GitHub repository releases as a file system, enabling direct access to release assets through a familiar directory structure. -::: - -::: zh-CN GitHub Releases 驱动允许您将 GitHub 仓库的发布版本挂载为文件系统,通过熟悉的目录结构直接访问发布资源。 -::: - -## API Rate Limits { lang="en" } - -## API 速率限制 { lang="zh-CN" } -::: en -::: warning Rate Limits +## API 速率限制 -- **Unauthenticated requests**: 60 requests per hour -- **Authenticated requests** (with token): 5,000 requests per hour - -We recommend using a personal access token to avoid hitting rate limits. -::: - -::: zh-CN ::: warning 速率限制 - **未认证请求**:每小时 60 次请求 @@ -50,32 +21,10 @@ We recommend using a personal access token to avoid hitting rate limits. 建议使用个人访问令牌以避免触及速率限制。 ::: -## Repository Configuration { lang="en" } - -## 仓库配置 { lang="zh-CN" } - -### Basic Usage { lang="en" } +## 仓库配置 -### 基本用法 { lang="zh-CN" } +### 基本用法 -::: en -**Single Repository Mount** - -Mount a single repository to the root directory: - -```text -OpenListTeam/OpenList -``` - -This is equivalent to: - -```text -/:OpenListTeam/OpenList -``` - -::: - -::: zh-CN **单个仓库挂载** 将单个仓库挂载到根目录: @@ -90,32 +39,8 @@ OpenListTeam/OpenList /:OpenListTeam/OpenList ``` -::: - -### Multiple Repositories { lang="en" } - -### 多个仓库 { lang="zh-CN" } - -::: en -**Mount to Subdirectories** - -You can mount multiple repositories to different subdirectories: - -```text -/openlist-gh:OpenListTeam/OpenList -/openlist-frontend-gh:OpenListTeam/OpenList-Frontend -``` - -The leading `/` is optional: - -```text -openlist-gh:OpenListTeam/OpenList -openlist-frontend-gh:OpenListTeam/OpenList-Frontend -``` - -::: +### 多个仓库 -::: zh-CN **挂载到子目录** 您可以将多个仓库挂载到不同的子目录: @@ -132,31 +57,10 @@ openlist-gh:OpenListTeam/OpenList openlist-frontend-gh:OpenListTeam/OpenList-Frontend ``` -::: - -## Configuration Options { lang="en" } - -## 配置选项 { lang="zh-CN" } - -### Personal Access Token { lang="en" } - -### 个人访问令牌 { lang="zh-CN" } - -::: en -**When to use:** - -- Required for accessing private repositories -- Recommended to avoid rate limiting issues - -**How to get:** +## 配置选项 -1. Log in to GitHub -2. Visit: https://github.com/settings/tokens -3. Generate a new token with appropriate permissions +### 个人访问令牌 -::: - -::: zh-CN **使用场景:** - 访问私有仓库时必需 @@ -168,40 +72,8 @@ openlist-frontend-gh:OpenListTeam/OpenList-Frontend 2. 访问:https://github.com/settings/tokens 3. 生成具有适当权限的新令牌 -::: - -### Show All Versions { lang="en" } - -### 显示所有版本 { lang="zh-CN" } +### 显示所有版本 -::: en -**Disabled (default):** - -``` -openlist/ -├── openlist-linux-amd64.tar.gz -└── openlist-windows-amd64.zip -``` - -**Enabled:** - -``` -openlist/ -├── v3.41.0/ -│ ├── openlist-linux-amd64.tar.gz -│ └── openlist-windows-amd64.zip -├── v3.40.0/ -│ ├── openlist-linux-amd64.tar.gz -│ └── openlist-windows-amd64.zip -└── v3.39.4/ - ├── openlist-linux-amd64.tar.gz - └── openlist-windows-amd64.zip -``` - -When enabled, all available release versions are displayed in separate directories. -::: - -::: zh-CN **禁用(默认):** ``` @@ -226,34 +98,9 @@ openlist/ ``` 启用后,所有可用的发布版本都会显示在单独的目录中。 -::: -### Pagination Control (Show All Versions) { lang="en" } +### 分页控制(显示所有版本) -### 分页控制(显示所有版本) { lang="zh-CN" } - -::: en -When "Show All Versions" is enabled, you can control the number of releases fetched via pagination: - -- **`per_page`**: Number of releases per page (default: `30`, max: `100`) -- **`max_page`**: Maximum number of pages to fetch (`0` means unlimited) - -These settings help you avoid hitting API rate limits when repositories have a large number of releases. - -**Example:** - -To fetch at most 2 pages with 50 releases per page (up to 100 releases total): - -```text -per_page = 50 -max_page = 2 -``` - -::: tip -A single unauthenticated request counts as 1 toward the 60 requests/hour limit. With `per_page=100` and `max_page=1`, fetching releases for a single repository costs only 1 request. -::: - -::: zh-CN 当启用"显示所有版本"时,您可以通过分页控制获取的发布数量: - **`per_page`**:每页 release 数量(默认:`30`,最大:`100`) @@ -274,39 +121,8 @@ max_page = 2 单次未认证请求计为 1 次,占每小时 60 次限额。设 `per_page=100`、`max_page=1` 时,获取单个仓库的 release 仅消耗 1 次请求。 ::: -### Show README Files { lang="en" } - -### 显示 README 文件 { lang="zh-CN" } - -::: en -**Disabled (default):** - -``` -openlist/ -├── openlist-linux-amd64.tar.gz -└── openlist-windows-amd64.zip -``` - -**Enabled:** - -``` -openlist/ -├── v3.41.0/ -│ ├── openlist-linux-amd64.tar.gz -│ └── openlist-windows-amd64.zip -├── v3.40.0/ -│ ├── openlist-linux-amd64.tar.gz -│ └── openlist-windows-amd64.zip -├── LICENSE -├── README.md -└── README_cn.md -``` - -::: tip -When enabled, README and LICENSE files from the repository are displayed alongside releases. However, folder size and modification time information will not be shown. -::: +### 显示 README 文件 -::: zh-CN **禁用(默认):** ``` @@ -334,40 +150,8 @@ openlist/ 启用后,仓库的 README 和 LICENSE 文件会与发布版本一起显示。但是,不会显示文件夹大小和修改时间信息。 ::: -## GitHub Proxy Settings { lang="en" } +## GitHub 代理设置 -## GitHub 代理设置 { lang="zh-CN" } - -::: en -Use GitHub proxy services to accelerate downloads in regions with limited GitHub access. - -**Configuration:** -Replace the GitHub domain with a proxy service URL: - -```text -https://gh-proxy.com/https://github.com -``` - -**Available Proxy Services:** - -| Service | URL | -| ----------- | ----------------------------------------- | -| GH-Proxy | `https://gh-proxy.com/https://github.com` | -| GHProxy.net | `https://ghproxy.net/https://github.com` | -| GHFast | `https://ghfast.top/https://github.com` | - -Example: - -``` -Before: https://github.com/owner/repo/releases/download/v1.0/file.zip -After: https://ghproxy.net/https://github.com/owner/repo/releases/download/v1.0/file.zip -``` - -::: warning -Proxy services are third-party and may have varying availability and performance. -::: - -::: zh-CN 使用 GitHub 代理服务来加速在 GitHub 访问受限地区的下载。 **配置方法:** @@ -396,33 +180,7 @@ https://gh-proxy.com/https://github.com 代理服务为第三方提供,可用性和性能可能存在差异。 ::: -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -442,5 +200,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/google_drive.md b/pages/guide/drivers/google_drive.md index 9d50e0dea..bca3b2cc4 100644 --- a/pages/guide/drivers/google_drive.md +++ b/pages/guide/drivers/google_drive.md @@ -1,46 +1,17 @@ --- -title: - en: Google Drive - zh-CN: 谷歌云盘 -icon: iconfont icon-state -# This control sidebar order top: 597 -# A page can have multiple categories categories: - guide - drivers --- -::: en - -Official website: https://drive.google.com/ - -- Supports Team Drive (Enter the Team Drive directory ID for the Root Folder ID) - -::: - -::: zh-CN +# 谷歌云盘 官网:https://drive.google.com/ - 支持团队盘(根目录ID填写团队盘的目录ID) -::: - -## 1. Prerequisites { lang="en" } - -## 1. 必要条件 { lang="zh-CN" } - -::: en - -1. The machine on which the OpenList service is deployed must be able to connect to Google Drive. - -2. The Google Drive API service must be enabled. For details on how to enable it, please refer to [Enabling the Google Drive API](#_2-1-enable-google-drive-api). - - > For detailed steps, see [Google Workspace > Google Drive > Quick Start Guide](https://developers.google.com/workspace/drive/api/quickstart/js). - -::: -::: zh-CN +## 1. 必要条件 1. 搭建 OpenList 服务的机器必须能够连接到 Google 网盘。 @@ -48,26 +19,9 @@ Official website: https://drive.google.com/ > 详细步骤可参考 [Google Workspace > Google Drive > 快速入门指南](https://developers.google.com/workspace/drive/api/quickstart/js) -::: - -## 2. Preparation { lang="en" } - -## 2. 准备接入 { lang="zh-CN" } - -### 2.1. Enable Google Drive API { lang="en" } - -### 2.1. 启用 Google Drive API 的 API { lang="zh-CN" } - -::: en - -1. On the [Guide Page](https://developers.google.com/workspace/drive/api/quickstart/js), locate the `Enable the API` button and click to proceed to the quick activation interface. Follow the instructions and confirm the settings to complete the activation process. +## 2. 准备接入 -2. You can also visit the [Google Drive API Management Interface](https://console.cloud.google.com/apis/library/drive.googleapis.com). Find the `Enable` button at the top and click it; wait for the activation to complete. - -3. `Quotas and System Limits Management`(optional): Switch to the `Quotas and System Limits` tab and set appropriate quotas and limits based on your usage and requirements. - -::: -::: zh-CN +### 2.1. 启用 Google Drive API 的 API 1. 在[指南页面](https://developers.google.com/workspace/drive/api/quickstart/js)中找到`启用API`按钮,点击进入快捷开启界面,点击下一步以及确认后,即可完成开启。 @@ -75,33 +29,7 @@ Official website: https://drive.google.com/ 3. `配额和系统限制管理`(可选):切换到`配额和系统限制`选项卡,根据你的使用情况和需求,设置合适的配额和限制。 -::: - -### 2.2. Creating an OAuth Client (Optional; not required if using OpenList, a public welfare server, or a self-built server) { lang="en" } - -### 2.2. 创建 OAuth 客户端(可选,如果使用OpenList/公益服务器/自建服务器,则不用创建) { lang="zh-CN" } - -::: en - -1. On the [Credentials Page](https://console.cloud.google.com/apis/credentials?hl=en), click the `Create Credentials` dropdown menu and select and click `OAuth client ID`. - > If prompted with `To create an OAuth client ID, you must first configure the consent screen`, click the `Configure consent screen` button, and complete the creation of the `Brand Information` on the subsequent page, selecting `External` for the `Audience`. After completion, return to the Credentials page to continue creating the OAuth client ID. - > ![google-oauth-00](/img/drivers/google/google-oauth-eng-00.png) -2. In the pop-up window, select `Web Application` and enter the name of your application type. -3. For the `Authorized redirect URIs`, enter `https://api.oplist.org/googleui/callback`. If you are using a self-built service or a non-profit organization, please enter the corresponding domain name, for example, `https://your-domain.com/googleui/callback`. -4. Click the `Create` button to complete the creation of the OAuth client. Note: Make sure to copy and save the `Client ID` and `Client Secret`; you will need them later on. - - ![google-oauth-01](/img/drivers/google/google-oauth-eng-01.png) - -5. Add yourself as a test user: On the left side, find the `Audience` menu and click to enter it. Then, locate `Test Users` and click the `+ Add users` button. Enter your Google account email address and click `Save`. - - ![google-oauth-02](/img/drivers/google/google-oauth-eng-02.png) - -6. After completing the testing, you can publish your application: On the left side, find the `Audience` menu and click to enter it. Then, locate `Publication Status` and click the `Publish app` button. Confirm the publication to complete the application release process. - - ![google-oauth-03](/img/drivers/google/google-oauth-eng-03.png) - -::: -::: zh-CN +### 2.2. 创建 OAuth 客户端(可选,如果使用OpenList/公益服务器/自建服务器,则不用创建) 1. 在[凭据界面](https://console.cloud.google.com/apis/credentials?hl=zh-cn)中,点击`创建凭据`下拉菜单,选择和点击`OAuth 客户端 ID` @@ -125,50 +53,7 @@ Official website: https://drive.google.com/ ![google-oauth-03](/img/drivers/google/google-oauth-03.png) -::: - -### 2.3. Get `Access Token` and `Refresh Token` { lang="en" } - -### 2.3. 获取`访问密钥`和`刷新密钥` { lang="zh-CN" } - -::: en - -1. Open the [OpenList Google Authorization Page](https://api.oplist.org/). **⚠️ If you are using a public or self-hosted server, please proceed with that server.** - -2. In the dropdown menu on the page, select `GoogleDrive Login`. - - ![google-00-l](/img/drivers/google/google-00-l.png#light) - ![google-00-d](/img/drivers/google/google-00-d.png#dark) - -3. If you created an OAuth Client ID in the previous steps, please enter the Client ID and Client Secrets in the input fields below. - - ![google-01-01-l](/img/drivers/google/google-01-01-l.png#light) - ![google-01-01-d](/img/drivers/google/google-01-01-d.png#dark) - - Otherwise, check the box ☑️ to use the API provided by OpenList. **⚠️ If you are using a public or self-hosted server, the built-in Client ID and Client Secrets of that server will be used.** - - ![google-01-02-l](/img/drivers/google/google-01-02-l.png#light) - ![google-01-02-d](/img/drivers/google/google-01-02-d.png#dark) - -4. Click the `Get Authorization` button. The system will redirect you to the Google Authorization page. Log in to your Google account and authorize OpenList to access your Google Drive. - - ![google-02](/img/drivers/google/google-02.png) - -5. If a message appears stating `This app has not been verified by Google`, click `Advanced` and then click `Go to oplist.org (insecure)` to continue. (If you are using a public or self-hosted server, the domain name here should match the actual domain name of the service; please verify this carefully.) - - ![google-03](/img/drivers/google/google-03.png) - -6. Pay attention to the permission information on the authorization page (within the red box in the image below). Make sure that the permission granted is only for accessing your Google Drive files. If you find that the permission includes access to something other than just your Google Drive files in the public service, please report an issue at [OpenListTeam/OpenList-Docs/issues](https://github.com/OpenListTeam/OpenList-Docs/issues). - - ![google-04](/img/drivers/google/google-04.png) - -7. After authorization is successful, the system will provide you with a `Refresh Token` and an `Access Token`. Please copy and save these tokens, as they will be used in subsequent configurations. - - ![google-05-l](/img/drivers/google/google-05-l.png#light) - ![google-05-d](/img/drivers/google/google-05-d.png#dark) - -::: -::: zh-CN +### 2.3. 获取`访问密钥`和`刷新密钥` 1. 打开[OpenList Google 授权页面](https://api.oplist.org/) **⚠️如果使用公益服务器/自建服务器,请访问公益服务器/自建服务器** @@ -204,78 +89,17 @@ Official website: https://drive.google.com/ ![google-05-l](/img/drivers/google/google-05-l.png#light) ![google-05-d](/img/drivers/google/google-05-d.png#dark) -::: - -## 3. Add Google Drive in OpenList { lang="en" } +## 3. 在 OpenList 中添加谷歌云盘 -## 3. 在 OpenList 中添加谷歌云盘 { lang="zh-CN" } - -### 3.1. Configuration { lang="en" } - -### 3.1. 配置说明 { lang="zh-CN" } - -#### 3.1.1. Root Folder ID { lang="en" } - -#### 3.1.1. **根文件夹 ID** { lang="zh-CN" } - -::: en - -Similar to Aliyun Drive, it is the last string of the official website URL, such as: - -![google](/img/drivers/google/googledrive-dir.png) +### 3.1. 配置说明 -::: -::: zh-CN +#### 3.1.1. **根文件夹 ID** 与阿里云盘类似,官网 URL 的最后一个字符串,如: ![google](/img/drivers/google/googledrive-dir.png) -::: - -### 3.2. Start Adding { lang="en" } - -### 3.2. 开始添加 { lang="zh-CN" } - -::: en - -1. Open the management interface of OpenList and click on `Storage` in the left menu. - -2. On the Storage List page, click the `Add Storage` button in the top right corner. - -3. Select `Google Drive` as the drive. - - ![google-06-l](/img/drivers/google/google-06-l.png#light) - ![google-06-d](/img/drivers/google/google-06-d.png#dark) - -4. Enter the mount path, for example: `google-drive`. - -5. In the `Root Folder ID` field, enter the root folder ID obtained earlier; if you are using the root directory, enter `root`. - -6. In the `Refresh Token` field, enter the refresh token obtained earlier (if not available, refer to [Preparing for Integration](#_2-preparation). - -7. If you are using the OAuth client ID and key provided by OpenList (or a public welfare server/self-built server), follow steps 7.1 and 7.2: - - 7.1. Check the `Use online API` option to indicate that you want to use the online API provided by OpenList. - - 7.2. Enter `https://api.oplist.org/googleui/renewapi` as the API URL; if it’s a public welfare server/self-built server, enter the corresponding server address. - - ![google-07-d](/img/drivers/google/google-07-d.png#dark) - ![google-07-l](/img/drivers/google/google-07-l.png#light) - -8. If you are using your own OAuth client ID and key, follow steps 8.1 and 8.2: - - 8.1. Uncheck the `Use online API` option to indicate that you are using your own OAuth client ID and key. - - 8.2. Enter your OAuth client ID in the `Client id` field and your OAuth Client secrets in the `Client secret` field. - - ![google-08-l](/img/drivers/google/google-08-l.png#light) - ![google-08-d](/img/drivers/google/google-08-d.png#dark) - -9. Click the `Add` button to complete the addition of Google Drive. - -::: -::: zh-CN +### 3.2. 开始添加 1. 打开 OpenList 的管理界面,点击左侧菜单中的`存储`。 @@ -312,28 +136,9 @@ Similar to Aliyun Drive, it is the last string of the official website URL, such 9. 点击`添加`按钮,完成谷歌云盘的添加。 -::: +## 4. 关于`Use online api`选项的说明 -## 4. About `Use online api` option { lang="en" } - -## 4. 关于`Use online api`选项的说明 { lang="zh-CN" } - -### 4.1. AccessToken refresh method with own keys { lang="en" } - -### 4.1. 自身有密钥的情况下,AccessToken刷新的方式 { lang="zh-CN" } - -::: en - -```mermaid -sequenceDiagram - participant OpenList - participant GoogleDrive - OpenList->>GoogleDrive: Provide refresh token + built-in client ID and secret - GoogleDrive->>OpenList: Return new access token + refresh token -``` - -::: -::: zh-CN +### 4.1. 自身有密钥的情况下,AccessToken刷新的方式 ```mermaid sequenceDiagram @@ -344,30 +149,7 @@ sequenceDiagram ``` -::: - -### 4.2. AccessToken refresh method without own keys { lang="en" } - -### 4.2.自身没有密钥的情况下,AccessToken刷新的方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: How to refresh AccessToken via OnlineAPI? ---- -sequenceDiagram - participant OpenList - participant OnlineAPI - participant GoogleDrive - OpenList->>OnlineAPI: Provide refresh token - OnlineAPI->>GoogleDrive: Provide refresh token + built-in client ID and secret - GoogleDrive->>OnlineAPI: Return new access token + refresh token - OnlineAPI->>OpenList: Return new access token + refresh token -``` - -::: -::: zh-CN +### 4.2.自身没有密钥的情况下,AccessToken刷新的方式 ```mermaid --- @@ -383,33 +165,7 @@ sequenceDiagram OnlineAPI->>OpenList: 返回新的访问令牌+刷新令牌 ``` -::: - -## 5. The default download method used { lang="en" } - -## 5. 默认使用的哪种下载方式? { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[Local Proxy]:::someclass==Default===>a2[User Device] - classDef someclass fill:#f96 - b1[Proxy URL]-.Alternative.->a2[User Device] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 5. 默认使用的哪种下载方式? ```mermaid --- @@ -427,5 +183,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/google_photos.md b/pages/guide/drivers/google_photos.md index c3276d82e..acbf127df 100644 --- a/pages/guide/drivers/google_photos.md +++ b/pages/guide/drivers/google_photos.md @@ -1,73 +1,26 @@ --- -title: - en: Google Photos - zh-CN: 谷歌相册 -icon: iconfont icon-state -# This control sidebar order top: 596 -# A page can have multiple categories categories: - guide - drivers --- -::: zh-CN +# 谷歌相册 + ::: tip 由于[谷歌相册官方于今年4月份对Library API做出的调整](https://developers.google.com/photos/support/updates?hl=zh-cn),将谷歌相册成功挂载到openlist后,实际只能查看通过openlist上传的照片,而不能再像曾经一样查看用户通过谷歌相册官方app、网站上传的照片。 ::: -::: en -::: tip -Due to the adjustments made by Google Photos to its Library API in April this year (https://developers.google.com/photos/support/updates?hl=en), after successfully mounting Google Photos to openlist, you can actually only view the photos uploaded through openlist. You can no longer view the photos uploaded by users via the official Google Photos app or website as you could before. -::: - -## 根文件夹 ID { lang="zh-CN" } -## Root folder file_id { lang="en" } +## 根文件夹 ID -::: en -The root directory is root, and other directory IDs are unknown (~~It seems that a separate album cannot be mounted~~ It has not been resolved yet [**Reference**](https://github.com/alist-org/alist/discussions/3264#discussioncomment-4874536)) -::: - -::: zh-CN 根目录root,其他目录ID未知( ~~貌似不能挂载单独的相册~~ 已解决下面有介绍) -::: - -## 获取 client_id, client_secret { lang="zh-CN" } - -## Get client_id, client_secret { lang="en" } - -::: en - -Get the tool https://api.oplist.org - -How to get your own `client_id (client ID) client_secret (client secret key)` Go to [**Google Drive**](./google_drive.md) which article -::: -::: en -::: tip - -Tip This app has not been verified by Google - -You have been authorized to use an app that is currently in beta. Do not proceed unless you know the developer who invited you. -Let's click **Continue** on the left +## 获取 client_id, client_secret -Remember to check this option, otherwise an error will be reported, as explained in the following precautions -::: -::: en - -It is more convenient now. In fact, when obtaining Google cloud disk authorization, select the album permission at the same time, and the token obtained at that time can also be used to mount the album~ kill two birds with one stone - -Remember to enable **`Photos Library API`** - -- https://console.cloud.google.com/apis/library/photoslibrary.googleapis.com - -::: -::: zh-CN 获取工具 https://api.oplist.org 如何获取自己的 `client_id(客户端ID) client_secret(客户端秘钥)` 去看[**谷歌网盘**](./google_drive.md)哪一篇 -::: -::: zh-CN + ::: tip 提示 此应用未经 Google 验证 @@ -82,32 +35,15 @@ Remember to enable **`Photos Library API`** ::: -::: zh-CN - 现在更加方便了,其实获取谷歌云盘的时候勾选了相册到时候获取到的token也可以拿来挂载相册~一举两得 记得打开 **`Photos Library API`** 哦~ - **https://console.cloud.google.com/apis/library/photoslibrary.googleapis.com?project=instant-shard-350513** -::: - -## 挂载单个相册目录 { lang="zh-CN" } - -## Mount a single album directory { lang="zh-CN" } +## 挂载单个相册目录 -::: en - -According to [above](https://api.oplist.org) obtained **client ID** and **secret key** and **refresh_token** - -We fill in **https://api.oplist.org** to get a single catalog ID for each of our Google Photos - -- Root file ID (that is, the album ID): default root, display all, if you only want to display a single album, you can fill in the album ID you want to display -- After we fill in, there will also be an **`access_token`** below. This can also be filled in the OpenList background refresh token option and used. The refresh token obtained at the beginning can be used for both of them- [Explanation](https://github.com/alist-org/alist/discussions/3264#discussioncomment-5051171) - -::: - -::: zh-CN +## Mount a single album directory 根据[上面](https://api.oplist.org)获取到的**客户端ID**和**秘钥**和**刷新令牌** @@ -116,22 +52,7 @@ We fill in **https://api.oplist.org** to get a single catalog ID for each of our - 根文件ID(就是相册ID):默认root,展示全部,若只想展示单个相册,可以填写你想展示的相册ID - 在我们填写后下方也会出现一个 **`access_token`** 这个也可以填写到刷新令牌里面前使用和之前获取到的,这两个都可以用 - [解释](https://github.com/alist-org/alist/discussions/3264#discussioncomment-5051171) -::: - -## 两个注意事项: { lang="zh-CN" } - -## Two notes: { lang="en" } - -::: en - -1. failed get objs: failed to list objs: Request had insufficient authentication scopes.: [] - - It's because you didn't check the album permission (see the picture mentioned above) `view and organize the content in your Google Photos media library, and upload content to the media library when obtaining authorization. Learn more` - - You get the `access_token` and the `refresh_token` without declearing the scope of `https://www.googleapis.com/auth/photoslibrary` -2. failed get objs: failed to list objs: Photos Library API has not been used in project ... before or it is disabled. Enable it by visiting then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.: [] - - because **`Photos Library API`** is not enabled in your project, Google Photos API - -::: -::: zh-CN +## 两个注意事项: 1. failed get objs: failed to list objs: Request had insufficient authentication scopes.: [] - 是因为在获取授权的时候没有勾选 相册权限(查看上面提到的图片) `查看和整理您的Google相册媒体库中的内容,向媒体库上传内容。` @@ -139,33 +60,7 @@ We fill in **https://api.oplist.org** to get a single catalog ID for each of our 2. failed get objs: failed to list objs: Photos Library API has not been used in project ... before or it is disabled. Enable it by visiting then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.: [] - 是因为对应的项目没有开启 **`Photos Library API`** 谷歌相册API -::: - -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -183,5 +78,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/guangyapan.md b/pages/guide/drivers/guangyapan.md index 64f7d38ed..5fa12032d 100644 --- a/pages/guide/drivers/guangyapan.md +++ b/pages/guide/drivers/guangyapan.md @@ -1,209 +1,78 @@ --- -title: - en: GuangYaPan - zh-CN: 光鸭云盘 -icon: iconfont icon-state -# This control sidebar order top: 701 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide --- +# 光鸭云盘 + -:::: en -:::: warning -This driver uses a **two-stage SMS login**. You need to provide a valid `client_id` before login. After completing the SMS login, `access_token` and `refresh_token` are saved automatically, and you can log in again with just `access_token` or `refresh_token`. -:::: -:::: zh-CN :::: warning 该驱动使用**两步短信登录**。登录前需要提供有效的 `client_id`。短信登录完成后会自动保存 `access_token` 与 `refresh_token`,之后只需凭 `access_token` 或 `refresh_token` 即可再次登录。 :::: -## 1. Add in OpenList { lang="en" } - -## 1. 在 OpenList 中添加 { lang="zh-CN" } - -### Client ID { lang="en" } +## 1. 在 OpenList 中添加 -### 客户端 ID { lang="zh-CN" } +### 客户端 ID -:::: en -**Required**. Fill in the `client_id` for the GuangYaPan API. This field must be provided, otherwise storage initialization will fail. -:::: -:::: zh-CN **必填**。填入光鸭云盘 API 的 `client_id`,此项必须填写,否则存储初始化会失败。 -:::: -### Phone Number { lang="en" } +### 手机号 -### 手机号 { lang="zh-CN" } - -:::: en -The phone number used for SMS login, e.g. `+86 13800000000`. -:::: -:::: zh-CN 用于短信登录的手机号,例如 `+86 13800000000`。 -:::: - -### Captcha Token { lang="en" } -### 验证码令牌 { lang="zh-CN" } +### 验证码令牌 -:::: en -Captcha token required by `/v1/auth/verification`. Leave it empty if no captcha is triggered. -:::: -:::: zh-CN `/v1/auth/verification` 接口所需的验证码令牌。若未触发验证码可留空。 -:::: -### Send Code { lang="en" } +### 发送验证码 -### 发送验证码 { lang="zh-CN" } - -:::: en -Set it to `true` and save to send the SMS code. It auto-resets to `false` after sending. -:::: -:::: zh-CN 设为 `true` 并保存即可发送短信验证码,发送后会自动重置为 `false`。 -:::: - -### Verify Code { lang="en" } -### 短信验证码 { lang="zh-CN" } +### 短信验证码 -:::: en -The SMS verification code you received. Fill it in and save to finish the login. -:::: -:::: zh-CN 收到的短信验证码。填写后保存即可完成登录。 -:::: -### Verification ID { lang="en" } +### 验证 ID -### 验证 ID { lang="zh-CN" } - -:::: en -Auto-generated after sending the SMS code. Do not edit it manually. -:::: -:::: zh-CN 发送短信验证码后自动生成,请勿手动修改。 -:::: - -### Access Token { lang="en" } -### 访问令牌 { lang="zh-CN" } +### 访问令牌 -:::: en -Bearer access token. It is saved automatically after SMS login. If you already have a valid `access_token` or `refresh_token`, you can fill it directly instead of doing the SMS login. -:::: -:::: zh-CN Bearer 访问令牌。短信登录成功后会自动保存。如果已经持有有效的 `access_token` 或 `refresh_token`,可以直接填写而无需短信登录。 -:::: -### Refresh Token { lang="en" } +### 刷新令牌 -### 刷新令牌 { lang="zh-CN" } - -:::: en -Refresh token for auto-login and auto-refresh. Saved automatically after SMS login. -:::: -:::: zh-CN 用于自动登录与自动刷新的刷新令牌,短信登录成功后会自动保存。 -:::: - -### Root Folder Path { lang="en" } -### 根文件夹路径 { lang="zh-CN" } +### 根文件夹路径 -:::: en -Full path in the GuangYaPan cloud drive, e.g. `/Movies/Anime`. Leave it empty to use the root directory. -:::: -:::: zh-CN 光鸭云盘中的完整路径,例如 `/电影/动漫`。留空则使用根目录。 -:::: -### Device ID { lang="en" } +### 设备 ID -### 设备 ID { lang="zh-CN" } - -:::: en -Optional custom device id (32 hex chars). Auto-generated when empty. -:::: -:::: zh-CN 可选的自定义设备 ID(32 位十六进制字符),留空时自动生成。 -:::: - -### Device Sign { lang="en" } -### 设备签名 { lang="zh-CN" } +### 设备签名 -:::: en -Optional custom `X-Device-Sign` header. Generated from `device_id` when empty. -:::: -:::: zh-CN 可选的自定义 `X-Device-Sign` 请求头,留空时根据 `device_id` 自动生成。 -:::: - -### Page Size / Order By / Sort Type { lang="en" } - -### 分页大小 / 排序字段 / 排序方向 { lang="zh-CN" } - -:::: en - -- `Page Size`: file list page size, default `100`. -- `Order By`: sort field used by the file list, options `0,1,2,3,4`, default `3`. -- `Sort Type`: sort direction used by the file list, options `0,1`, default `1`. -:::: -:::: zh-CN +### 分页大小 / 排序字段 / 排序方向 - `分页大小`:文件列表每页数量,默认 `100`。 - `排序字段`:文件列表使用的排序字段,可选 `0,1,2,3,4`,默认 `3`。 - `排序方向`:文件列表使用的排序方向,可选 `0,1`,默认 `1`。 -:::: - -## 2. SMS Login Steps { lang="en" } - -## 2. 短信登录步骤 { lang="zh-CN" } - -:::: en - -1. Fill in `client_id` and `phone_number` (and `captcha_token` if needed). -2. Set `send_code` to `true`, then save. The storage will enter a "SMS sent" status and the `verification_id` is generated automatically. -3. Fill in the received `verify_code`, then save again to finish the login. The `access_token` and `refresh_token` are saved automatically. - -:::: -:::: zh-CN +## 2. 短信登录步骤 1. 填写 `client_id` 与 `phone_number`(如需要同时填写 `captcha_token`)。 2. 将 `send_code` 设为 `true` 并保存,存储会进入「短信已发送」状态,并自动生成 `verification_id`。 3. 填写收到的 `verify_code` 后再次保存,即可完成登录,`access_token` 与 `refresh_token` 会自动保存。 -:::: - -## 3. Offline Download { lang="en" } - -## 3. 离线下载 { lang="zh-CN" } - -:::: en -GuangYaPan supports calling its own offline download function from OpenList. +## 3. 离线下载 -1. Mount a GuangYaPan storage. -2. In the backend **Settings** → **Other**, set the GuangYaPan temporary directory (choose any folder of this account). -3. Go back to the frontend, enter the target folder, and choose **GuangYaPan** in the offline download option at the lower right corner. - -- Supports adding offline tasks via URL (e.g. `http`, `magnet` links). - -:::: -:::: zh-CN 光鸭云盘支持在 OpenList 中调用其自身的离线下载功能。 1. 挂载光鸭云盘存储。 @@ -212,25 +81,9 @@ GuangYaPan supports calling its own offline download function from OpenList. - 支持通过链接(如 `http`、`magnet`)添加离线任务。 -:::: - -## 4. Precautions { lang="en" } - -## 4. 注意事项 { lang="zh-CN" } - -:::: en - -- `client_id` is required, otherwise storage initialization will fail. -- The `verify_code` is one-time use and will be cleared after a successful login. -- The driver does not support overwriting uploads (same-name files will not overwrite). -- Login priority: `access_token` → `refresh_token` → SMS login. - -:::: -:::: zh-CN +## 4. 注意事项 - `client_id` 为必填项,否则存储初始化会失败。 - `verify_code` 为一次性使用,登录成功后会被清空。 - 该驱动不支持覆盖上传(同名文件不会覆盖)。 - 登录优先级:`access_token` → `refresh_token` → 短信登录。 - -:::: diff --git a/pages/guide/drivers/halalcloud.md b/pages/guide/drivers/halalcloud.md index 9afe3d636..7e7042721 100644 --- a/pages/guide/drivers/halalcloud.md +++ b/pages/guide/drivers/halalcloud.md @@ -1,71 +1,22 @@ --- -title: - en: halalcloud - zh-CN: 6盘 -icon: iconfont icon-state -# This control sidebar order top: 679 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' - - '官方' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en - -- `6盘(halalcloud)` Official website:[https://2dland.cn](https://2dland.cn/) - - Cloud Login:https://drive.2dland.cn -- Official announcement, document address:https://2dland.yuque.com/r/organizations/homepage - -::: - -::: zh-CN +# 6盘 - `6盘(halalcloud)` 官方网站:[https://2dland.cn](https://2dland.cn/) - 网盘登录:https://drive.2dland.cn - 官方公告、文档地址:https://2dland.yuque.com/r/organizations/homepage -::: - -## Root folder file_id { lang="en" } - -## 根文件夹 ID { lang="zh-CN" } +## 根文件夹 ID -::: en -Top address bar path,Root folder file_id is:`/` -Subfolder: `/A folder/C folder/C folder` -::: -::: zh-CN 顶部地址栏路径,根文件夹是:`/` 子文件夹:`/A文件夹/C文件夹/C文件夹` -::: -## Fill in the example { lang="en" } - -## 填写示例 { lang="zh-CN" } - -::: en - -On the HalalCloud (6 盘) website, go to `User Center` and navigate to `Authorization Management`. Enter your HalalCloud account password to verify your identity. - -Create a new authorization (you can name it anything you like). Click `Confirm`, then copy and save the `Client ID` and `Client Secret`. - -![halalcloud_add_authorization](/img/drivers/halalcloud/halalcloud_add_authorization.png) - -In the OpenList admin panel, go to `Storage` and add a new driver. Select `HalalCloudOpen`, and enter the `Client ID` and `Client Secret` obtained from the previous step. - -::: -::: zh-CN +## 填写示例 在 `6盘官网`内点击`用户中心`,进入`授权管理`页面,输入 6 盘账户密码验证身份。 @@ -75,22 +26,7 @@ In the OpenList admin panel, go to `Storage` and add a new driver. Select `Halal 在 OpenList 后台的存储页添加驱动,选择`HalalCloudOpen`,在下方填写`客户端 ID` 和`客户端密钥`,分别对应上方的值即可。 -::: - -## Other parameters { lang="en" } - -## 其它参数 { lang="zh-CN" } - -::: en - -- `Upload thread`: Upload threads (Default: 3, Range: 1-32) - -- `Host`: (Provided by default, no input required) - -- `WebDAV Policy`: Default is `302 Redirect`. Switch to `Local Proxy` if you encounter any issues. - -::: -::: zh-CN +## 其它参数 - `Upload thread`:上传线程(默认为 3,范围 1-32) @@ -98,35 +34,7 @@ In the OpenList admin panel, go to `Storage` and add a new driver. Select `Halal - `WebDav策略`:默认为`302重定向`,若有问题可以修改为`本地代理` -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -146,5 +54,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/ilanzou.md b/pages/guide/drivers/ilanzou.md index 2bec65511..857e1eb28 100644 --- a/pages/guide/drivers/ilanzou.md +++ b/pages/guide/drivers/ilanzou.md @@ -1,91 +1,30 @@ --- -title: - en: iLanZou - zh-CN: 蓝奏云优享版 -icon: iconfont icon-state -# This control sidebar order top: 687 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 蓝奏云优享版 + **https://ilanzou.com** -## Root folder ID { lang="en" } - -## 根文件夹ID { lang="zh-CN" } +## 根文件夹ID -::: en -root folder ID the default is `0`,Other directory ID View the figure below obtaining method -![LanZoufolder_id](/img/drivers/lanzou/ilanzou_folder.png) -::: -::: zh-CN 根目录ID,默认为`0`,其它目录ID查看下图获取方式 ![LanZoufolder_id](/img/drivers/lanzou/ilanzou_folder.png) -::: -## username、password { lang="en" } +## 账户、密码 -## 账户、密码 { lang="zh-CN" } - -::: en -Just fill in your own NewLanzou Cloud Account Password -::: -::: zh-CN 填写自己的蓝奏云优享版帐号密码 -::: - -## Known issues { lang="en" } -## 已知问题 { lang="zh-CN" } +## 已知问题 -::: en -The file size returned by iLanZou is in Kilo Bytes, not Bytes. Therefore, you cannot accurately determine if a file has been modified based on its size. Please pay attention to the configuration of your sync software. -::: -::: zh-CN 蓝奏云优享版返回的文件大小非 Bytes,而是 Kilo Bytes,因此无法使用文件大小准确判断一个文件是否被修改,需要注意同步软件的配置。 -::: - -## The default download method used { lang="en" } -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -105,5 +44,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/kodbox.md b/pages/guide/drivers/kodbox.md index e394df736..879c02ecd 100644 --- a/pages/guide/drivers/kodbox.md +++ b/pages/guide/drivers/kodbox.md @@ -1,46 +1,16 @@ --- -title: - en: KodBox - zh-CN: 可道云 -icon: iconfont icon-state -# This control sidebar order top: 496 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -Use this driver to mount a KodBox netdisk space to OpenList. -::: -::: zh-CN -使用本驱动可以挂载 KodBox 的网盘空间到 OpenList。 -::: - -## Root folder path { lang="en" } - -## 根文件夹ID { lang="zh-CN" } - -::: en -Suppose you have a network disk space named `My Files`. If you remount the contents of the network disk space, you need to obtain the path corresponding to `My File`; if you re-display a name in the network disk space for For the directory of `abc`, you must obtain the path corresponding to `My File/abc`, and so on. +# 可道云 -Example: How to get the path of KodBox network disk space `My File`, the result path is `{source:5}` +使用本驱动可以挂载 KodBox 的网盘空间到 OpenList。 -Open the KodBox network disk space in the browser. In the console mode, you can see the path corresponding to the network disk space. Do not leave it blank. -![kodbox_root_folder_path](/img/drivers/kodbox/kodbox_root_folder_path_en.jpg) -::: +## 根文件夹ID -::: zh-CN 假设你有一个网盘空间名为 `个人空间`,如果你只想挂载该网盘空间的内容,就要获取`个人空间`对应的 path;如果你只想展示该网盘空间内一个名为 `abc` 的目录,就要获取`个人空间/abc`对应的 path ,以此类推。 示例:如何获取 KodBox 网盘空间`个人空间`的path,得出path为`{source:5}` @@ -48,76 +18,23 @@ Open the KodBox network disk space in the browser. In the console mode, you can 在浏览器中打开 KodBox 网盘空间,在控制台模式下即可看到网盘空间对应的 path,不可留空不填。 ![kodbox_root_folder_path](/img/drivers/kodbox/kodbox_root_folder_path_zh.jpg) -::: - -## Address { lang="en" } -## 地址 { lang="zh-CN" } +## 地址 -::: en -Your KodBox server address, e.g. - -- `https://kodcloud.cc` -- `http://192.168.1.24:8000` - -::: -::: zh-CN 你的 KodBox 服务器地址,形如: - `https://kodcloud.cc` - `http://192.168.1.24:8000` -::: - -## Username { lang="en" } - -## 用户名 { lang="zh-CN" } +## 用户名 -::: en -The email or username used to log in to your KodBox server. -::: -::: zh-CN 用于登录你的 KodBox 服务器的邮箱或用户名。 -::: -## Password { lang="en" } +## 密码 -## 密码 { lang="zh-CN" } - -::: en -The password for your email or username. -::: -::: zh-CN 邮箱或用户名对应的密码。 -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -137,5 +54,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/lanzou.md b/pages/guide/drivers/lanzou.md index 66ed5e745..63188662c 100644 --- a/pages/guide/drivers/lanzou.md +++ b/pages/guide/drivers/lanzou.md @@ -1,45 +1,14 @@ --- -title: - en: LanZou Cloud - zh-CN: 蓝奏云盘 -icon: iconfont icon-state -# This control sidebar order top: 688 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - - -::: en -Lanzuo Network Disk has three types of **modes (types)**, which are **Accounts**, **Cookie** and **Url** - -- **Account**: You can display all the files of your own cloud disk, you only need to fill in the account password, which will automatically help you refresh the cookie - -- **Cookie**: All files in the network disk can be displayed, but the cookie of the network disk needs to be provided - -- **Url**: only display the content in the link (you can mount it without providing a cookie) - -::: tip - -It is recommended to use the **Account** method to mount - -- Cookie mode needs to manually update Cookie +# 蓝奏云盘 -::: + -::: zh-CN 蓝奏网盘挂载有三种**模式(类型)**,分别是 **账户**,**Cookie** 和 **链接** - **账户**:可以显示自己网盘所有文件,只需要填写帐号密码,会自动帮你刷新cookie @@ -55,21 +24,8 @@ It is recommended to use the **Account** method to mount ::: -## root folder ID { lang="en" } - -## 根文件夹 ID { lang="zh-CN" } - -::: en -Lanzuo cloud disk root directory ID, the default is `-1`,If you use `Url`Type to mount the root folder ID, please see the third item of [Fill in instructions](#fill-in-instructions) - -- **Additional Folder ID** - - Two ways to obtain (see the picture below) - - ![](/img/drivers/lanzou/lanzou2.gif) - -::: +## 根文件夹 ID -::: zh-CN 蓝奏云盘根目录ID,默认为`-1`,如果使用`链接`类型挂载根文件夹ID请看[填写说明](#填写说明)的第三条 - **其他文件夹ID** @@ -77,102 +33,34 @@ Lanzuo cloud disk root directory ID, the default is `-1`,If you use `Url`Type ![](/img/drivers/lanzou/lanzou2.gif) -::: - -## Account { lang="en" } - -## 账户 { lang="zh-CN" } +## 账户 -::: en -Just fill in your own Lanzou Cloud Account Password -::: -::: zh-CN 只需要填写自己的蓝奏云帐号密码 -::: -## Cookie { lang="en" } +## Cookie -## Cookie { lang="zh-CN" } - -::: en -Log in to your [**Lanzuo Cloud Disk**](https://pc.woozooo.com/) account, then F12 to open the developer mode, you can find `Cookie`, there is no specific one, such as the left side of the picture Just click one and there will be `Cookie` in the blue part of the right picture. -![lanzou](/img/drivers/lanzou/lanzou1.png) -::: -::: zh-CN 登录自己的 [**蓝奏云盘**](https://pc.woozooo.com/) 账号,然后F12打开开发者模式,就能找到 `Cookie`,没有具体的,例如图片中左侧的随便点一个然后有右图蓝色部分的 `Cookie` 即可. ![lanzou](/img/drivers/lanzou/lanzou1.png) -::: -## Share link and share password { lang="en" } - -## 分享链接和分享密码 { lang="zh-CN" } - -::: en - -- **Share link**: Randomly generate a share link in the Lanzuo cloud disk - - Small tip: **lanzouX** in the link, the last default **X** content sometimes cannot be opened in some areas, you can try to change it yourself, for example, change to i, x, u, t, etc. Anyway, try it out for yourself. -- **Share password**: the password for the link - -::: -::: zh-CN +## 分享链接和分享密码 - **分享链接**:在蓝奏云盘内随便生成一个分享链接 - 小提示:链接里面的 **lanzouX**,最后这个默认的 **X**内容有时候有些地区打不开,可以自己更换一下试试看比如换成 i,x,u,t 等等等反正好多自己试试看就行. - **分享密码**:链接的密码 -::: - -## Repair file info { lang="en" } +## 修复文件信息 -## 修复文件信息 { lang="zh-CN" } - -::: en -If you need **WebDav** service, you need to open it -::: -::: zh-CN 需要 **WebDav** 服务的,需要打开它 -::: - -## error message { lang="en" } - -## 错误信息 { lang="zh-CN" } -::: en -Use cookie type or account type to add prompt `not find file page param` error because lanzou changed the domain name +## 错误信息 -- Solution: It is recommended to use account type to add - - Change the sharing link to `https://wwop.lanzoul.com` and save it. If it still doesn't work, refresh the lower right corner. - - lanzoul will also make changes in the future - -::: - -::: zh-CN 使用Cookie类型或者账户类型添加提示`not find file page param`错误是因为蓝奏云更换了域名 - 解决方案:建议使用账户类型添加 - 把分享链接改成 `https://wwop.lanzoul.com`保存即可,如果还是不可以,右下角刷新一下。 - 后续蓝奏可能还会更改。 -::: - -## Fill in instructions { lang="en" } - -## 填写说明 { lang="zh-CN" } - -::: en - -1. **Acount mode**: Type selects `account`, fill in` account` and `password` options, and the `root folder ID` (the default is all files of the root directory) -2. **Cookie mode**: Type selects `Cookie`, fill in` Cookie`, and the `root folder ID` (the default is all files of the root directory) -3. **URL mode**: Type selects `Url`, fill in` ShareUrl` and `root folder ID` and`Share Password (if there is a password)` - - The sharing link is :point_right: https://xxx.lanzou.com/aaabbbccc :point_left: in this format - - Fill in `ShareUrl`: https://xxx.lanzou.com - - Fill in `root folder ID`: aaabbbccc - - Share Password: if there is a password - ![lanzou](/img/drivers/lanzou/lanzou_add.png) - -::: - -::: zh-CN +## 填写说明 1. **账户模式**:类型选择`账户`,填写`账户`和`密码`选项,以及`根文件夹ID`(选填,默认为根目录全部文件) 2. **Cookie模式**:类型选择`Cookie`,填写`Cookie`,以及`根文件夹ID`(选填,默认为根目录全部文件) @@ -183,35 +71,7 @@ Use cookie type or account type to add prompt `not find file page param` error b - 在`分享密码`填写:有密码就写没有就不用写 ![lanzou](/img/drivers/lanzou/lanzou_add.png) -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -231,5 +91,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/lenovo_nas_share.md b/pages/guide/drivers/lenovo_nas_share.md index 026ea5447..b837257d2 100644 --- a/pages/guide/drivers/lenovo_nas_share.md +++ b/pages/guide/drivers/lenovo_nas_share.md @@ -1,130 +1,43 @@ --- -title: - en: Lenovo Nas Share - zh-CN: 联想家庭储存链接分享 -icon: iconfont icon-state -# This control sidebar order top: 494 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - +# 联想家庭储存链接分享 -::: en -Need to purchase Lenovo devices **https://pc.lenovo.com.cn** -::: + -::: zh-CN 需要购买联想设备 **https://pc.lenovo.com.cn** -::: - -## Root Folder ID { lang="en" } - -## 根文件夹ID { lang="zh-CN" } -::: en -Root Folder ID: Leave it blank +## 根文件夹ID -Subfolder ID: Get as shown in the picture -::: - -::: zh-CN 根文件夹:留空 子文件夹:按图片所示获取 -::: ![](/img/drivers/lenovonasshare/lenovonasshare_fileid.png) -## Share ID and Share Password { lang="en" } - -## 分享ID和分享密码 { lang="zh-CN" } - -::: en -Example of share link: https://siot-share.lenovo.com.cn/s/#/eb.3N93ZbJsaAjerjdm4N Extraction code: `e5eu` - -- **Share ID**: Fill in the sharing link and automatically extract the string `eb.3N93ZbJsaAjerjdm4N` at the end of the sharing link -- **Share Password**: The extraction code `e5eu` +## 分享ID和分享密码 -::: - -::: zh-CN 分享链接链接示例:https://siot-share.lenovo.com.cn/s/#/eb.3N93ZbJsaAjerjdm4N 提取码:`e5eu` - **分享ID**:填写分享链接,自动提取分享链接中末尾的字符串 `eb.3N93ZbJsaAjerjdm4N` - **分享密码**:提取码 `e5eu` -::: - -## Host Address { lang="en" } - -## 主机地址{ lang="zh-CN" } - -::: en -The default uses the public network: **https://siot-share.lenovo.com.cn** +## 主机地址 -(Not recommended) If you are using a local network, you can change it to the internal network address of the Lenovo device: **http://192.168.XX.XX** -::: - -::: zh-CN 默认使用公网的:**https://siot-share.lenovo.com.cn** (不推荐)如果你使用局域网的可以改成联想设备内网地址:**http://192.168.XX.XX** -::: - -## Show Root Folder { lang="en" } -## 显示根文件夹 { lang="zh-CN" } +## 显示根文件夹 -::: en -If unchecked and `Share ID` is empty, the folder ID of the first-level folder is automatically filled in. -Taking the above picture as an example, the contents of the `OpenList` folder are directly displayed without displaying the `OpenList` folder. -::: - -::: zh-CN 若取消勾选且 `分享ID` 为空,自动填入第一级文件夹的文件夹ID。 以上图为例,直接显示 `OpenList` 文件夹中的内容,不显示 `OpenList` 文件夹。 -::: - -## The default download method used { lang="en" } -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -144,5 +57,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/local.md b/pages/guide/drivers/local.md index c807e0c7f..504347978 100644 --- a/pages/guide/drivers/local.md +++ b/pages/guide/drivers/local.md @@ -1,80 +1,27 @@ --- -title: - en: Local storage - zh-CN: 本机存储 -icon: iconfont icon-state -# This control sidebar order top: 999 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -Support mounting the directory of the local machine. -::: -::: zh-CN -支持挂载本机的目录。 -::: - -## Root folder path { lang="en" } - -## 根文件夹ID { lang="zh-CN" } +# 本机存储 -::: en -The path of folder you wanted to mount. For example: +支持挂载本机的目录。 -- Linux: `/root` -- Windows: `C:` +## 根文件夹ID -::: -::: zh-CN 您要挂载的文件夹的路径。 例如: - Linux: `/root` - Windows: `C:` -::: - -## Local storage video thumbnail { lang="en" } - -## 本地存储视频封面 { lang="zh-CN" } +## 本地存储视频封面 -::: en -You need to use the `ffmpeg` tool to add. -::: - -::: zh-CN 需要使用 `ffmpeg` 工具来添加. -::: - -## Local storage PDF thumbnail on macOS { lang="en" } -## macOS 本地存储 PDF 缩略图 { lang="zh-CN" } +## macOS 本地存储 PDF 缩略图 -::: en -On macOS, the Local storage driver can generate thumbnails from the first page of PDF files using the system Quick Look tool. - -To enable this feature: - -1. Enable `Thumbnail`. -2. Enable `PDF thumbnail`. -3. It is recommended to configure `Thumb cache folder` to avoid rendering the same PDF repeatedly. - -This feature is disabled by default and is only available when OpenList runs on macOS. Rendering is performed on cache misses and may consume additional CPU and memory. -::: - -::: zh-CN 在 macOS 上,本机存储驱动可以调用系统 Quick Look 工具,为 PDF 文件生成首页缩略图。 启用方法: @@ -84,27 +31,9 @@ This feature is disabled by default and is only available when OpenList runs on 3. 建议配置 `缩略图缓存目录(Thumb cache folder)`,避免重复渲染同一 PDF 文件。 该功能默认关闭,并且仅在 OpenList 运行于 macOS 时可用。缓存未命中时会执行渲染,可能额外消耗 CPU 和内存。 -::: - -## Recycle bin path { lang="en" } - -## 回收站路径 { lang="zh-CN" } - -::: en -path to recycle bin, delete permanently if empty or keep 'delete permanently' - -If you fill in this path, you will move the file into the folder when deleting the local storage file, so that you have a chance to regret it. - -The method of filling in the above -mentioned mounting path is different from different system filling methods. - -If you don’t know if you fill in it correctly, you can test it yourself first and then use the production environment to use it yourself. -- Linux: `/root` -- Windows: `C:` - -::: +## 回收站路径 -::: zh-CN 回收站的路径,如果为空则永久删除或保持“永久删除” 如果填写此路径在删除本地存储文件时会将文件移动到此文件夹內,让你有一次后悔的机会。 @@ -116,33 +45,7 @@ If you don’t know if you fill in it correctly, you can test it yourself first - Linux: `/root` - Windows: `C:` -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -160,5 +63,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/mediafire.md b/pages/guide/drivers/mediafire.md index 2d79a46a7..fb13e3395 100644 --- a/pages/guide/drivers/mediafire.md +++ b/pages/guide/drivers/mediafire.md @@ -1,75 +1,30 @@ --- -title: - en: MediaFire - zh-CN: Mediafire -icon: iconfont icon-state -# This control sidebar order top: 520 -# A page can have multiple categories categories: - guide - drivers --- -::: en -Mount [MediaFire](https://www.mediafire.com/) cloud storage. +# Mediafire -::: -::: zh-CN 挂载 [MediaFire](https://www.mediafire.com/) 云存储。 -::: +## 根文件夹ID -## 根文件夹ID { lang="zh-CN" } - -## Root folder path { lang="en" } - -::: en -The default root directory for the path to mount mediafire, default is `/`. - -::: -::: zh-CN 要挂载的Mediafire路径,默认为 `/`。 -::: - -## Session Token { lang="zh-CN" } +## Session Token -## Session Token { lang="en" } - -::: en -Session Token - -Open the browser developer tools, visit [mediafire.com](https://www.mediafire.com/), log in to your account, go to the `Network` tab, open `get_session_token.php`, find `session_token` in the `Response`, and copy its value to fill in. - -![Mediafire Session Token](/img/drivers/mediafire/mediafire-sessiontoken.png) - -::: -::: zh-CN 会话令牌 打开浏览器开发者工具,访问 [mediafire.com](https://www.mediafire.com/),登录账号后,查看`Network`(网络)Tab,打开`get_session_token.php`,在`Response`(响应)中找到`session_token`,复制其值填入即可。 ![Mediafire Session Token](/img/drivers/mediafire/mediafire-sessiontoken.png) -::: - -## Cookie { lang="zh-CN" } - -## Cookie { lang="en" } - -::: en -Web Cookie - -Open the browser developer tools, visit [mediafire.com](https://www.mediafire.com/), log in to your account, go to the `Network` tab, open `get_session_token.php`, find `Cookie` in the `Header`, and copy its value to fill in. - -![Mediafire Cookie](/img/drivers/mediafire/mediafire-cookie.png) +## Cookie -::: -::: zh-CN 网页Cookie 打开浏览器开发者工具,访问 [mediafire.com](https://www.mediafire.com/),登录账号后,查看`Network`(网络)Tab,打开`get_session_token.php`,在`Header`(标头)中找到`Cookie`,复制其值填入即可。 ![Mediafire Cookie](/img/drivers/mediafire/mediafire-cookie.png) -::: diff --git a/pages/guide/drivers/mediatrack.md b/pages/guide/drivers/mediatrack.md index 350c5d9cb..3528eb7ce 100644 --- a/pages/guide/drivers/mediatrack.md +++ b/pages/guide/drivers/mediatrack.md @@ -1,98 +1,30 @@ --- -title: - en: Mediatrack - zh-CN: 分秒帧 -icon: fa-solid fa-x -# This control sidebar order top: 680 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -**https://app.mediatrack.cn** -::: -::: zh-CN -**https://app.mediatrack.cn** -::: +# 分秒帧 -### Access token { lang="en" } +**https://app.mediatrack.cn** -## 访问令牌 { lang="zh-CN" } +## 访问令牌 -::: en -You can get it in request header after logging in -![token](/img/drivers/mediatrack-token.png) -::: -::: zh-CN 登录后可以在请求头中获取 ![token](/img/drivers/mediatrack-token.png) -::: - -### Project id { lang="en" } -## 项目编号 { lang="zh-CN" } +## 项目编号 -::: en -Get from official website url: -![Project id](/img/drivers/mediatrack-projectid.png) -::: -::: zh-CN 从官网网址获取: ![Project id](/img/drivers/mediatrack-projectid.png) -::: -### Root folder id { lang="en" } +## 根文件夹 ID -## 根文件夹 ID { lang="zh-CN" } - -::: en -Get it from the request after logging in -![id](/img/drivers/mediatrack-rootid.png) -::: -::: zh-CN 登录后从请求中获取 ![id](/img/drivers/mediatrack-rootid.png) -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -112,5 +44,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/mega.md b/pages/guide/drivers/mega.md index 9ea9f8783..c5e0dc837 100644 --- a/pages/guide/drivers/mega.md +++ b/pages/guide/drivers/mega.md @@ -1,147 +1,50 @@ --- -title: - en: MEGA Disk - zh-CN: MEGA Disk -icon: iconfont icon-state -# This control sidebar order top: 592 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' - - '官方' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -::: warning -Due to MEGA requires decryption to be performed on the OpenList server backend, **using proxy URL** with this driver cannot avoid server traffic consumption. -::: -::: zh-CN +# MEGA Disk + ::: warning 由于MEGA需要在OpenList服务器后端实现解密,在该驱动**使用代理地址**不能避免服务器流量消耗。 ::: -::: en -MEGA official website: **https://mega.nz/login** -::: -::: zh-CN MEGA官网:**https://mega.nz/login** -::: - -### Mail { lang="en" } -### 邮箱 { lang="zh-CN" } +### 邮箱 -::: en -MEGA login account -::: -::: zh-CN MEGA登录账号 -::: - -### password { lang="en" } -### 密码 { lang="zh-CN" } +### 密码 -::: en -login password -::: -::: zh-CN 登录密码
-::: - -## 2FA { lang="en" } -## 两步验证 { lang="zh-CN" } +## 两步验证 -::: en -Non -filling options,If you open 2FA, you need to fill in it,If you don’t use 2FA, you don’t need to fill in it -::: -::: zh-CN 非必填选项,如果开启了需要填写,没开启的不需要填写 -::: - -### Two fa secret { lang="en" } -### 两步验证秘钥 { lang="zh-CN" } +### 两步验证秘钥 -::: en -**https://mega.nz/fm/account/security/two-factor-authentication** -Click to enable `Enable two-factor authentication (2FA)` and then see a QR code, there is a string of letters below the QR code, This is `Two fa secret`, please record before scanning the code and do not lose it, Should the `Two-factor authentication (2FA) secret` be lost, you can unbinding and then retrieve a new 2FA QR code for setup. -![](/img/drivers/mega/mega_2fa.png) -::: -::: zh-CN **https://mega.nz/fm/account/security/two-factor-authentication** 点击启用 `启用双重验证(2FA)`然后会看到一个二维码,二维码下方有一串字母,这个就是`2FA验证秘钥`,请在扫码绑定前记录一下切勿丢失,`2FA验证秘钥`如果丢失可以解绑重新获取一个新的 ![](/img/drivers/mega/mega_2fa.png) -::: - -### Two fa code { lang="en" } -### 两步验证代码 { lang="zh-CN" } +### 两步验证代码 -::: en -To enable 2-factor authentication, you need to install a app that support 2FA (such as [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2), [Microsoft Authenticator](https://support.microsoft.com/en-us/account-billing/download-and-install-the-microsoft-authenticator-app-351498fc-850a-45da-b7b6-27e523b8702a)), on your phone. -Then login to OpenList manage and goto `Profile` page, click `Enable 2FA` button, scan the QR code with your 2FA app, and enter the code generated by your 2FA app. -Scan the QR appearing on the screen. At the same time, this QR code is also [two-fa-secret](#two-fa-secret) -![](/img/drivers/mega/mega_2fa_code.png) -
-::: -::: zh-CN 要启用双因素身份验证,需要在手机上安装支持 TOTP 的验证器,例如 [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2),[Microsoft Authenticator](https://support.microsoft.com/zh-cn/account-billing/%E4%B8%8B%E8%BD%BD%E5%B9%B6%E5%AE%89%E8%A3%85microsoft-authenticator%E5%BA%94%E7%94%A8-351498fc-850a-45da-b7b6-27e523b8702a) 扫描屏幕出现的二维码就可以,同时这个二维码也是[两步验证秘钥](#两步验证秘钥) ![](/img/drivers/mega/mega_2fa_code.png)
-::: -## Fill in example: { lang="en" } +## 填写示例 -## 填写示例 { lang="zh-CN" } - -::: en -![](/img/drivers/mega/mega_add.png) -
-::: -::: zh-CN ![](/img/drivers/mega/mega_add.png)
-::: - -### The default download method used { lang="en" } -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -159,5 +62,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/misskey.md b/pages/guide/drivers/misskey.md index 8b7d28775..2fa23981d 100644 --- a/pages/guide/drivers/misskey.md +++ b/pages/guide/drivers/misskey.md @@ -1,45 +1,17 @@ --- -title: - en: Misskey Drive - zh-CN: Misskey Drive -icon: iconfont icon-state -# This control sidebar order top: 588 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -## Instance URL { lang="en" } +# Misskey Drive -## 实例地址 { lang="zh-CN" } +## 实例地址 -::: en -The URL of your Misskey instance, whether self-hosted or in use. -For example, the official instance at `https://misskey.io` -::: -::: zh-CN 自行部署的或正在使用的Misskey实例地址 例如官方实例`https://misskey.io` -::: -## Token { lang="en" } +## Token -## Token { lang="zh-CN" } - -::: en -Visit `https://{misskey-instance}/settings/api` to generate an access token. It requires the `read:drive` and `write:drive` permissions. -::: -::: zh-CN 前往`https://{misskey-instance}/settings/api`生成访问token,需要`read:drive`及`write:drive`权限。 -::: diff --git a/pages/guide/drivers/mopan.md b/pages/guide/drivers/mopan.md index 3814f25e8..00e006d12 100644 --- a/pages/guide/drivers/mopan.md +++ b/pages/guide/drivers/mopan.md @@ -1,114 +1,34 @@ --- -title: - en: MoPan - zh-CN: 四川电信魔盘 -icon: iconfont icon-state -# This control sidebar order top: 691 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - - -::: en -MoPan address:**https://mopan.sc.189.cn/mopan/#/downloadPc** +# 四川电信魔盘 -- There is no web version, only `Android`, `iOS`, `PC-Win64bit`, `iPad`, and `TV`. - -::: + -::: zh-CN 四川电信魔盘官网链接:**https://mopan.sc.189.cn/mopan/#/downloadPc** - 没有网页端,只有`Android`,`IOS`,`PC-Win64位`,`iPad`,`TV` -::: +## Sms code -## Sms code { lang="en" } - -## Sms code { lang="zh-CN" } - -::: en -Enter the option of the mobile phone number and password when the first addition, and then enter the `SMS Code` input `send`, and then click Save to send it to you. -::: -::: zh-CN 第一次添加时先输入 手机号和密码的选项,然后在`Sms Code`输入 `send`,再点击保存会给你进行发短信,然后将验证码重新输入就可以添加 -::: - -## Root folder ID { lang="en" } - -## 根文件夹ID { lang="zh-CN" } -::: en -Do not fill in this option, it will automatically fill into the root directory +## 根文件夹ID -- Due to encrypted requests, an appropriate method for obtaining folder IDs has not yet been found - -::: -::: zh-CN 留空会自动填充为根目录 - 由于请求加密,暂时未想到合适的获取文件夹 ID 的方法 -::: - -### Tips { lang="en" } - -## 提示 { lang="zh-CN" } - -::: en - -1. `root folder ID`,`equipment information`does not need to be filled in, will automatically help you fill -2. If you enter the send in [SMS Code] (#SMS-Code), it is found that it has been saved,Please click Edit to enter the verification code received - -::: - -::: zh-CN +## 提示 1. `根文件夹ID`、`设备信息`不用填写,会自动帮你填充 2. 如果在[Sms Code](#sms-code)输入验证码后已经保存了,请进入编辑输入收到的验证码 -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -128,5 +48,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/onedrive.md b/pages/guide/drivers/onedrive.md index 345279756..0dd38fa35 100644 --- a/pages/guide/drivers/onedrive.md +++ b/pages/guide/drivers/onedrive.md @@ -1,25 +1,12 @@ --- -title: - en: OneDrive / Share - zh-CN: OneDrive / 分享 -icon: iconfont icon-state -# This control sidebar order top: 599 -# A page can have multiple categories categories: - guide - drivers --- -:::en -:::tip - -- If you have global administrator permissions for a non-home edition, you can use the [OneDrive APP](onedrive_app.md) driver. -- If your account does not support the API, (for example, the school account has not verified the administrator, or the administrator has disabled the API), then you can also mount it through webdav. For details, see [webdav](webdav.md) - -::: +# OneDrive / 分享 -:::zh-CN :::tip - 如果你拥有非家庭版的全局管理员权限,可使用 [OneDrive APP](onedrive_app.md) 驱动 @@ -27,25 +14,8 @@ categories: ::: -## 1. Mounting using Online API's default application { lang="en" } - -## 1. 使用在线 API 的默认应用挂载 { lang="zh-CN" } - -::: en -You do not need to create an application yourself by mounting in this way. - -1. Open and select the corresponding OneDrive version according to your account. +## 1. 使用在线 API 的默认应用挂载 -2. Check "Use parameters provided by OpenList", click "Get Token", then log in to the OneDrive account you want to mount. After authorization, return to the page to get the refresh token. - - ![Online Token Acquisition Tool](/img/drivers/OneDrive/online_api.png) - -3. Go to the storage management interface of OpenList, select the OneDrive driver, check "Use online API", fill in the refresh token and you can mount it. - - ![Backend Configuration Interface](/img/drivers/OneDrive/online_api_config.png) - -::: -::: zh-CN 通过这种方式挂载,您无需自行创建应用。 1. 打开 ,根据自己的账户选择对应的 OneDrive 版本。 @@ -58,43 +28,7 @@ You do not need to create an application yourself by mounting in this way. ![后端配置界面](/img/drivers/OneDrive/online_api_config.png) -::: - -## 2. Mounting by creating an application manually { lang="en" } - -## 2. 手动创建应用挂载 { lang="zh-CN" } - -::: en - -The application provided by OpenList API may experience issues such as request rate limits due to a high number of users. In this case, you can manually create an application. - -1. Navigate to the corresponding management page based on your account type. - - OneDrive Global:https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade - - OneDrive 21vianet:https://portal.azure.cn/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade - - OneDrive Germany:https://portal.microsoftazure.de/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade - - OneDrive US GOV:https://portal.azure.us/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade - -2. After logging in, select `Register Application`, enter `Name`, and select `Accounts and Individuals in Any Organization Directory` (note that you don't look at the location selection but the text here. Some people may be the middle option, don't select a single Tenant or other options, otherwise it will cause problems when logging in), enter the `Redirect URL` as `https://api.oplist.org/onedrive/callback`, click `Register`, and then you can get the `client_id`. - - ![client_id](/img/drivers/OneDrive/onedrive-register-app.png) - -3. After registering the application, select `Certificate and Password`, click `New Client Password`, enter a string of passwords, select the one with the longest time, and click `Add`. - - (Note: The password entered after adding will disappear, please record the value of client_secret) - - ![client_secret](/img/drivers/OneDrive/onedrive-new-password.png) - -4. Select `API Permissions`, click `MicroSoft Graph`, enter file in the `Select Permissions`, and check `Files.read` (Note: `Files.read` is a read-only minimum permission. The permission in the figure is larger, and the same can be done), click `Update Permission`. - - ![api](/img/drivers/OneDrive/onedrive-update-permission.png) - -5. Fill in the `client_id` and `client_secret` obtained in the previous step into page, click `Get Token`. - -6. Go to OpenList's add storage page, uncheck "Use online API", and fill in the obtained `client_id`, `client_secret`, `Callback URL`, and `Refresh Token` in OpenList. - -::: - -::: zh-CN +## 2. 手动创建应用挂载 OpenList 在线 API 提供的默认应用程序因为用户太多,可能存在请求速率限制等问题。此时您可以手动创建应用程序。 @@ -122,59 +56,22 @@ OpenList 在线 API 提供的默认应用程序因为用户太多,可能存在 6. 进入 OpenList 添加存储页面,取消勾选`使用在线API`,将获取到的 `client_id`、`client_secret`、`Callback URL`、`Refresh Token` 填入 OpenList 中 -::: - -## Parameters { lang="en"} - -## 参数 { lang="zh-CN"} +## 参数 -### Sharepoint site_id { lang="en" } +### SharePoint 站点 ID -### SharePoint 站点 ID { lang="zh-CN" } - -::: en -If you need to mount SharePoint, after completing the above steps, there will be an input field for the site address below the refresh token display. Enter the site address, click to get the `site_id`, and then fill the obtained information into the `site_id` field on the OpenList add storage page. Make sure `Is sharepoint` is enabled. -::: -::: zh-CN 如果需要挂载 SharePoint,完成上面的步骤后,在显示刷新令牌的界面的下面有输入站点地址,输入站点地址后点击获取 `site_id`,然后将获取到的信息填入 OpenList 添加存储页面的 `站点ID` 中,确保已启用 `是否Sharepoint`。 -::: - -### Root folder path { lang="en" } -### 根文件夹路径 { lang="zh-CN" } +### 根文件夹路径 -::: en -The default is `/`, if you need to customize, just fill in the path, starting from the root path, the same as the local path, such as `/test` -::: -::: zh-CN 默认为 `/`,如果需要自定义,就填路径就行,从根路径开始,和本地路径一样,比如 `/test` -::: - -### Chunk size { lang="en" } -### 分片大小 { lang="zh-CN" } +### 分片大小 -::: en -Upload chunk size (MiB). The default is `5`, which means `5 * 1024 * 1024 = 5,242,880` bytes. Make sure to use a size that is a multiple of 320 KiB (`327,680` bytes). -::: -::: zh-CN 上传分片大小(MiB),默认值为 `5`,即 `5 * 1024 * 1024 = 5,242,880` 个字节,需要确保使用 320 KiB(`327,680` 个字节)倍数的字节大小。 -::: -### Custom host { lang="en" } +### 自定义HOST -### 自定义HOST { lang="zh-CN" } - -::: en -Custom accelerated download link. This is the domain name of your reverse-proxied OneDrive download API (for example, for personal accounts: `my.microsoftpersonalcontent.com`). Only domain replacement is supported here, not path replacement. - -::: warning - -- Only hostname replacement is supported, not path. -- Be sure to properly isolate and only reverse proxy your own paths, otherwise your Cloudflare account may be banned! - -::: -::: zh-CN 自定义加速下载链接。即反代你的 OneDrive 下载 API (如个人版:`my.microsoftpersonalcontent.com`)的域名。 ::: warning @@ -184,33 +81,7 @@ Custom accelerated download link. This is the domain name of your reverse-proxie ::: -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -231,40 +102,14 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: - -## 3. OneDrive Share Url { lang="en" } - -## 3. OneDrive 分享 { lang="zh-CN" } +## 3. OneDrive 分享 -::: en -![api](/img/drivers/onedrive_app/od_share_add.png) -::: -::: zh-CN ![api](/img/drivers/onedrive_app/od_share_add.png) -::: - -### Url { lang="en" } - -### 链接 { lang="zh-CN" } - -::: en -The sharing link is the same as the example below and can be mounted. It can be obtained from E3, E5, A1, and A1P. - -```html -https://connecthkuhk-my.sharepoint.com/:f:/g/personal/jhyang13_connect_hku_hk/EsEgHtGOWbJImxop6tF15FIBIH-ihrjuDclbrbmwWfY_RA?e=s6fitN -``` - -If it is OneDrive personal version, it will not work. The link is as follows -```html -https://onedrive.live.com/?cid=64EA5FCC7735E8C6&id=64EA5FCC7735E8C6%2117289 -``` +### 链接 -::: -::: zh-CN 分享链接是这样的可以挂载,来自E3、E5、A1、A1P等 ```html @@ -277,44 +122,11 @@ https://connecthkuhk-my.sharepoint.com/:f:/g/personal/jhyang13_connect_hku_hk/Es https://onedrive.live.com/?cid=64EA5FCC7735E8C6&id=64EA5FCC7735E8C6%2117289 ``` -::: - -### Password { lang="en" } - -### 密码 { lang="zh-CN" } +### 密码 -::: en -It is the extraction code. If you have it, write it. If you don’t have it, don’t fill it in. -::: -::: zh-CN 就是提取码,如果有就写,如果没有就不用写 -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -332,5 +144,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/onedrive_app.md b/pages/guide/drivers/onedrive_app.md index 146e37f24..f3fda105b 100644 --- a/pages/guide/drivers/onedrive_app.md +++ b/pages/guide/drivers/onedrive_app.md @@ -1,39 +1,13 @@ --- -title: - en: OneDrive APP - zh-CN: OneDrive APP -icon: iconfont icon-state -# This control sidebar order top: 598 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -## Benefits and Precautions { lang="en" } +# OneDrive APP -## 好处以及注意事项 { lang="zh-CN" } - -::: en - -1. The advantage is that the administrator is only authorized to log in once, and the same is used when adding parameters later. When adding, everything is the same except for the mailbox. - - For example, E5 has 25 5T accounts. After you log in and initialize in advance, you can directly use the parameters before the CV. You don’t have to get the refresh token every time as before, and it will expire after a long time - - A1, A1P, etc. can distribute more accounts (500 - **unlimited**) -2. After the administrator authorizes in this way, he can view the account of each member in the organization at will, which will leak his own files - - It is recommended to apply for OneDrive E5 to get better - -::: -::: zh-CN +## 好处以及注意事项 1. 好处是只授权一次管理员登录,后续添加参数时用一样的,在添加时除了邮箱哪里不同,别的都一样 - 例如E5 25个5T的账号,你提前登录初始化完毕后,直接CV之前的参数即可,不用像以前一样每次都得去获取刷新令牌时间一长还失效 @@ -41,29 +15,10 @@ star: true 2. 这样的方式管理员授权后,可以随意查看组织中每个成员的账号,泄露了自己的文件 - 建议自己申请 OneDrive E5 去好一些 -::: -::: en -::: tip -It is not applicable to the home version. If you are in the configuration process, it does not match the options that appear in the tutorial. It may not be supported -::: -::: zh-CN ::: tip 不适用于家庭版,如果你在配置过程中,与教程中出现的选项不符可能是不支持 ::: -::: en -First log in with your personal or organizational account - - -Login to get our `tenant ID` - -![onedriveapp](/img/drivers/onedrive_app/onedrive_app0.png) - -To log in to OneDrive (Operated by 21Vianet), please use this link - - -::: -::: zh-CN 首先使用个人或者组织账号登录 @@ -75,73 +30,13 @@ To log in to OneDrive (Operated by 21Vianet), please use this link 世纪互联版本的 OneDrive 登录 -::: - -## Create process { lang="en" } -## 新建过程 { lang="zh-CN" } +## 新建过程 -::: en -::: danger Read the precautions carefully -Please read carefully the following **`serial numbers , view in sequence`** , if there is any error, please feedback in time -::: -::: zh-CN ::: danger 请仔细阅读注意事项 请仔细阅读下面的 **`序号,依次查看`** ,如有错误可及时反馈 ::: -::: en - -1. Register an application, choose the type yourself, it is recommended to choose the third -2. Redirect URL (callback parameter), select Web as the type, and fill in the parameters **https://entra.microsoft.com/TokenAuthorize** ,Otherwise, it will not be able to authorize later. For OneDrive (Operated by 21Vianet), please use: **https://entra.microsoftonline.cn/TokenAuthorize** -3. After filling out, click on the registration below and you can see it after jumping `client ID` - ![onedriveapp](/img/drivers/onedrive_app/onedrive_app1.png) - -Let's get a client secret key, just get it as shown in the figure below, remember `secret key` save in time, it will only appear once -![onedriveapp](/img/drivers/onedrive_app/onedrive_app3.png) - -Once you have obtained the client secret key, go to authorize the **`API`** to be used, and follow the picture below to get it -![onedriveapp](/img/drivers/onedrive_app/onedrive_app4.png) - -After the API is authorized, we go to authorize the organization application - -- Left sidebar ---> Enterprise applications ---> All applications ---> **{choose which application name we started to create}** ---> Permissions ---> Click for `xxxx `Grant admin consent - ![onedriveapp](/img/drivers/onedrive_app/onedrive_app5.png) - -matters needing attention - -- If you start to register the application, the callback parameters are not the same, you may get an error when you click Authorization, remember to write the same -- How to check whether the authorization is successful. If it is not authorized, the page will prompt `The application has not been found to have the authorization of the administrator's permission`. After the authorization is successful, refresh the page and you will see three API permissions, as shown in the figure below - - The above is what it looks like when it is not authorized, and the following is what it looks like after authorization (just refresh the page after authorization) - ![onedriveapp](/img/drivers/onedrive_app/onedrive_app7.png) - -From the beginning to the present, we have obtained the following parameters: tenant ID1, registered application2, client ID/secret key3, authorization API/administrator API4 -Then open the browser and finally authorize it through the following format, which are `tenant ID` `client ID` `Callback URL` Change to your own final login to receive - -```html -https://login.microsoftonline.com/{tenant ID or common}/adminConsent?client_id={client -ID}&redirect_uri={Callback URL} -``` - -For OneDrive (Operated by 21Vianet), please use: - -```html -https://login.partner.microsoftonline.cn/{tenant ID or common}/adminConsent?client_id={client -ID}&redirect_uri={Callback URL} -``` - -![onedriveapp](/img/drivers/onedrive_app/onedrive_app6.png) -If you log in according to the spelled link and find that you are redirected to a blank page, such as the sample code and picture below, the authorization is successful, don’t worry - -``` -https://entra.microsoft.com/TokenAuthorize?admin_consent=True&tenant={Tenant ID}# -``` - -![onedriveapp](/img/drivers/onedrive_app/od_app_ok.png) -::: - -::: zh-CN - 1. 注册一个应用,类型自己选,建议选第三吧 2. 重定向URL(回调参数),类型选择Web,参数填写: **https://entra.microsoft.com/TokenAuthorize** ,否则后面授权时无法授权。世纪互联版本的 OneDrive 请填写: **https://entra.microsoftonline.cn/TokenAuthorize** 3. 填好后点击下方的注册 跳转后就可以看到 `客户端ID` @@ -187,111 +82,29 @@ https://entra.microsoft.com/TokenAuthorize?admin_consent=True&tenant={租户ID}# ``` ![onedriveapp](/img/drivers/onedrive_app/od_app_ok.png) -::: -## Fill in the example { lang="en" } +## 填写示例 -## 填写示例 { lang="zh-CN" } - -::: en -Fill in the values obtained in the above process one by one. If you don’t know which email address it is, you can find it by [**view all users of the organization**](#view-all-users-of-the-organization) -![onedriveapp](/img/drivers/onedrive_app/onedrive_app_Denmo.png) -::: -::: zh-CN 将上述过程中获取得到的值依次填入即可,如果那个邮箱你不知道是什么了,可以通过[**查看组织所有用户**](#查看组织所有用户)来查找 ![onedriveapp](/img/drivers/onedrive_app/onedrive_app_Denmo.png) -::: -## View all users of the organization { lang="en" } - -## 查看组织所有用户 { lang="zh-CN" } +## 查看组织所有用户 -::: en -If you don't know how many users there are in your OneDrive organization, you can go to the link below to log in to the `admin` account to view -**https://admin.microsoft.com/Adminportal/Home?source=applauncher#/users** -![onedriveapp](/img/drivers/onedrive_app/onedrive_app_user.png) -::: -::: zh-CN 如果不知道自己的OneDrive组织有多少用户可以前往下面的链接登录 `admin`账号查看 **https://admin.microsoft.com/Adminportal/Home?source=applauncher#/users** ![onedriveapp](/img/drivers/onedrive_app/onedrive_app_user.png) -::: - -## Error message { lang="en" } -## 错误提示 { lang="zh-CN" } - -::: en - -- Tips:**Either scp or roles claim need to be present in the token** Error - because you didn't do it in step 4 `grant xxx administrator consent` caused by,Pay attention to the tips below the fourth step -- Tips:**failed get objs: failed to list objs: Unable to retrieve user's mysite URL** Error - The newly created `OneDrive` user account does not take effect in real time, Delay takes effect, wait for a few hours and try again [**Case**](https://github.com/alist-org/docs/discussions/189#discussioncomment-5928892) - -::: - -::: zh-CN +## 错误提示 - 提示:**Either scp or roles claim need to be present in the token** 错误 是因为你没有在第 4 步的时候进行 `代表xxxx授予管理员同意` 导致的,注意查看第四步下面的提示 - 提示:**failed get objs: failed to list objs: Unable to retrieve user's mysite URL** 错误 新建的 `OneDrive`用户账号不是实时生效,会延时生效等待几小时后试试看 [**案例**](https://github.com/alist-org/docs/discussions/189#discussioncomment-5928892) -::: - -## Add OneDrive_App accounts in batches { lang="en" } - -## 批量添加OneDrive_App账号 { lang="zh-CN" } - -::: en -software used:**https://github.com/yzbtdiy/alist_batch** -:::details Fill in the parameters. If you don’t understand, you can watch the video below. -Do not leave blank lines in the two configuration files, as this will also report an error -**config.yaml** - -```yaml -url: ALIST_URL -auth: - username: USERNAME - password: PASSWORD -token: ALIST_TOKEN -aliyun: - enable: false - refresh_token: ALI_YUNPAN_REFRESH_TOKEN -pikpak: - enable: false - username: PIKPAK_EMAIL - password: PIKPAK_PASSWORD -onedrive_app: - enable: true - region: global - tenants: - - id: 1 - client_id: client_id - client_secret: client_secret - tenant_id: tenant_id -``` - -**onedrive_app.yaml** - -```yaml -Category: - mount: 1:user1@xxx.onmicrosoft.com - mount2: 1:user2@xxx.onmicrosoft.com:/folder1 - mount3: 1:user2@xxx.onmicrosoft.com:/folder2 -Category2: - mount11: 1:user1@xxx.onmicrosoft.com - mount22: 1:user2@xxx.onmicrosoft.com:/folder1 - mount33: 1:user2@xxx.onmicrosoft.com:/folder2 -``` +## 批量添加OneDrive_App账号 -::: -::: en - -::: -::: zh-CN 使用的软件:**https://github.com/yzbtdiy/alist_batch** :::details 参数填写,看不懂可以看下方的视频 两个配置文件不要留空白空行哦,那样也会报错的 @@ -335,37 +148,10 @@ onedrive_app: ``` ::: -::: zh-CN - -::: - -## The default download method used { lang="en" } -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` + -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -385,5 +171,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/openlist.md b/pages/guide/drivers/openlist.md index e00c6b8c8..09464c2d6 100644 --- a/pages/guide/drivers/openlist.md +++ b/pages/guide/drivers/openlist.md @@ -1,158 +1,55 @@ --- -title: - en: OpenList - zh-CN: OpenList -icon: iconfont icon-state -# This control sidebar order top: 499 -# A page can have multiple categories categories: - guide - drivers --- -::: en -Support mounting OpenList directories running on other servers +# OpenList -::: -::: zh-CN 支持挂载其他服务器中运行的 OpenList 目录 -::: - -## 链接 { lang="zh-CN" } +## 链接 -## Url { lang="en" } - -::: en -OpenList link you want to mount - -::: -::: zh-CN 你要挂载的 OpenList 链接 -::: - -## 用户名 { lang="zh-CN" } - -## Username { lang="en" } - -::: en -username - -When `username&password` is not filled in, `guest` is used for guest access. +## 用户名 -::: -::: zh-CN 用户名 不填写 `用户名&密码` 时使用的是 `guest` 游客访问 -::: - -## 密码 { lang="zh-CN" } - -## Password { lang="en" } - -::: en -password +## 密码 -::: -::: zh-CN 密码 -::: - -## 根文件夹ID { lang="zh-CN" } - -## Root folder path { lang="en" } - -::: en -The default root directory for the path to mount to is`/` +## 根文件夹ID -::: -::: zh-CN 要挂载到的路径,默认根目录为 `/` -::: - -## 元信息密码 { lang="zh-CN" } - -## Meta password { lang="en" } - -::: en -The OpenList path you want to mount has a meta information password set. You need to know what the other party's password is set to see the file, otherwise it will be blank after entering - -If multiple folders have different password settings, the password you fill in can only enter the folder with this password, and those without this password cannot enter +## 元信息密码 -::: -::: zh-CN 你要挂载的 OpenList 路径设置了元信息密码,你要知道对方的密码设置的是什么才能看到文件,不然进去后是空白 如果多个文件夹密码设置的不一样,那么你填写的密码只能进入这个密码的文件夹,不是这个密码的进不去 -::: - -::: en -::: danger -If you first use the `username&password` method for mounting, and then switch to using the `metadata password` method for mounting, you need to manually clear the previously automatically filled `token`, otherwise you will still use the `username&password` method for mounting - -::: - -::: zh-CN ::: danger 若你先使用 `用户名&密码` 方式挂载,后面切换成了使用 `元信息密码` 方式挂载,你需要手动将之前自动填充的 `令牌` 清除,否则还是会使用 `用户名&密码` 方式挂载 ::: -## 令牌 { lang="zh-CN" } - -## Token { lang="en" } +## 令牌 -::: en -You don’t need to write, it will be automatically filled after filling in `Username & Password` and saving - -::: -::: zh-CN 可以不用写,在填写 `用户名&密码` 保存后会自动填充 -::: - -## 代理range { lang="zh-CN" } - -## Proxy Range { lang="en" } - -::: en -You need to enable `Web Proxy` or `Webdav Native Proxy` to take effect - -Mount '139Yun' on the OpenList of the server and go to 302. Then, locally mount the OpenList on the server through the OpenList enabled proxy to play videos,achieving the goal of not using server traffic. The CMCC mobile card can also be streamed free +## 代理range -::: -::: zh-CN 需要先启用 `Web代理` 或者 `Webdav本地代理` 才会生效 服务器的 OpenList 挂载 `139Yun` 走302,然后本地通过开启代理的 OpenList 挂载服务器上的 OpenList 播放视频等,达到不使用服务器流量的目的,移动卡还能免流 -::: - -## 错误提示 { lang="zh-CN" } +## 错误提示 -## Error message { lang="en" } - -::: en -::: danger -If the OpenList you mounted is "not" enabled Allow Mount, you will not be able to To mount, the following error is prompted - -```json -Failed init storage: the site does not allow mounted -failed get objs: storage not init: the site does not allow mounted -``` - -Solution: Use the allowed `username&password` provided by the other party for mounting - -::: - -::: zh-CN ::: danger 若是你挂载的 OpenList "未"开启允许挂载,那你将无法进行挂载,提示如下错误 @@ -165,20 +62,6 @@ failed get objs: storage not init: the site does not allow mounted ::: -:::en -::: danger - -If the other party does not enable the guest account access permission, an error will be prompted when mounting (as follows) - -```json -failed get objs: failed to list objs: request failed,code: 400, message: Key: 'LoginReq.Username' Error:Field validation for 'Username' failed on the 'required' tag -``` - -Solution: Use the allowed `username&password` provided by the other party for mounting - -::: - -::: zh-CN ::: danger 如果对方未开启游客账户访问权限挂载时会提示错误(如下) @@ -190,33 +73,7 @@ failed get objs: failed to list objs: request failed,code: 400, message: Key: 'L ::: -## 默认使用的下载方式 { lang="zh-CN" } - -## The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -236,5 +93,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/pikpak.md b/pages/guide/drivers/pikpak.md index c2ff677e7..fbcc40568 100644 --- a/pages/guide/drivers/pikpak.md +++ b/pages/guide/drivers/pikpak.md @@ -1,38 +1,12 @@ --- -title: - en: PikPak / Share - zh-CN: PikPak / 分享 -icon: iconfont icon-state -# This control sidebar order top: 595 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -::: danger - -1. `Pikpak`:Who makes the request, who can use it - - For example, if you build an OpenList on the server with IP `1.1.1.1`, but your own IP is `2.2.2.2`, you cannot play or download it. - - Or enable Proxy policy - -2. `PikPak Share`:There is a size limit. After the specified file size is exceeded, only 40%~50% can be played. - - The specific size of the file is currently unknown - -::: +# PikPak / 分享 -::: zh-CN ::: danger 1. `个人Pikpak`:谁发出请求谁能用 @@ -44,93 +18,36 @@ star: true ::: -## 1. PikPak { lang="en" } - -## 1. PikPak挂载 { lang="zh-CN" } +## 1. PikPak挂载 -### Username { lang="en" } +### 用户名 -### 用户名 { lang="zh-CN" } - -::: en -email or phone? -::: -::: zh-CN 邮件地址或者电话号码? -::: - -### Password { lang="en" } -### 密码 { lang="zh-CN" } +### 密码 -::: en -password -::: -::: zh-CN 密码 -::: -### Root folder id { lang="en" } +### 根文件夹ID -### 根文件夹ID { lang="zh-CN" } - -::: en -Can get with https://mypikpak.com/ , default `root`. - -![image.png](/img/drivers/pikpak.png) -::: -::: zh-CN 可以通过 https://mypikpak.com/ 获取,默认为 `root`。 ![image.png](/img/drivers/pikpak.png) -::: - -### Platform { lang="en" } - -### 平台 { lang="zh-CN" } - -::: en -It is not necessary to use it under normal circumstances, but you may need to use it when you cannot log in directly with your account and password. -- If you choose `android`, it will simulate the Android client for access. If you want to log in using the `Refresh token`, please perform packet-sniffing on the **official Android app**. +### 平台 -- If you choose `web`, it will simulate the official web version for access. If you want to log in using the `Refresh token`, please use the `Refresh token` obtained from the **web version**. - -::: -::: zh-CN 正常情况下不需要使用,遇到无法直接使用帐号密码登录的情况下可能需要使用 - 如果选择 `android`,则会模拟 安卓客户端 进行访问,如果想使用 `Refresh token` 进行登录,请对 `官方安卓端APP` 进行抓包 - 如果选择 `web`,则会模拟 官方网页端 进行访问,如果想使用 `Refresh token` 进行登录,请使用 `Web端` 获取的 `Refresh token` -::: - -### Refresh token { lang="en" } +### 刷新令牌 -### 刷新令牌 { lang="zh-CN" } - -::: en -After filling in the account and password, select `Oauth2` for `Refresh token method` and then save to automatically fill in the refresh token and device information. -::: -::: zh-CN 填写帐号和密码后,`刷新令牌方法`选择 `Oauth2` 然后保存就会自动填充刷新令牌、设备信息 -::: - -#### Get Refresh Token on Web { lang="en" } - -#### Web端 Refresh Token 的获取 { lang="zh-CN" } - -::: en -After logging into the official website, open the F12 console and navigate to the page shown in the image below (using Chrome as an example): - -![pikpak-token-1](/img/drivers/pikpak/pikpak-token-1.png) -Find the option that starts with `credentials`. Select it, and observe the information bar below. From there, you can retrieve the `Refresh token`, as shown in the image below: +#### Web端 Refresh Token 的获取 -![pikpak-token-2](/img/drivers/pikpak/pikpak-token-2.png) -::: -::: zh-CN 在官方网页登录后,打开F12控制台,进入下图中的页面(以 Chrome 为例) ![pikpak-token-1](/img/drivers/pikpak/pikpak-token-1.png) @@ -138,47 +55,15 @@ Find the option that starts with `credentials`. Select it, and observe the infor 找到以credentials开头的选项,选中后,观察下方的信息栏,从中可以获取到Refresh token,如下图所示 ![pikpak-token-2](/img/drivers/pikpak/pikpak-token-2.png) -::: - -### Disable media link { lang="en" } - -### 禁用媒体链接 { lang="zh-CN" } - -::: en - -The OpenList interface uses the playback interface. When the file resolution is too high or the file size is too large, Pikpak will automatically transcode, which may cause synchronization errors in other applications. Enabling this option will prevent using the playback interface to obtain the address. -According to the official limitations, when the video's bitrate exceeds 40 Mbps or the file size is greater than 50 GB, the system will automatically trigger transcoding. If the video already offers other resolution options, the "original quality" resolution will not be available. - -::: -::: zh-CN +### 禁用媒体链接 OpenList 接口使用的是播放接口。当文件分辨率过高或文件过大时,Pikpak 会自动进行转码,这可能导致其他应用在同步时出现错误。启用此选项将不使用播放接口获取地址. 根据官方限制,当视频的码率超过 40 Mbps 或文件大于 50 GB 时,系统会自动触发转码。若视频已提供其他清晰度选项,则不会提供“原画”清晰度。 -::: - -### Offline Download { lang="en" } - -### 离线下载 { lang="zh-CN" } - -::: en -support calling `Pikpak` offline download function in OpenList - -Select `Pikpak` in the lower right corner and select `Pikpak` for offline download options - -- Support: `magnet`, `http`, `ed2k` links -- Also supports: X, TikTok, Facebook, TG URL links +### 离线下载 -- Only Pikpak is supported for offline download. If it is not Pikpak, the following error message will be displayed, Although the offline download prompt was successfully added, an error will be prompted in the background. - - unsupported storage driver for offline download, only Pikpak is supported - - ![](/img/drivers/offline_download_error.png) - -::: -::: zh-CN 支持在OpenList调用`Pikpak`离线下载功能 右下角选择离线下载选项选择`Pikpak` @@ -191,56 +76,23 @@ Select `Pikpak` in the lower right corner and select `Pikpak` for offline downlo ![](/img/drivers/offline_download_error.png) -::: - -## 2. PikPak Share { lang="en" } +## 2. PikPak分享挂载 -## 2. PikPak分享挂载 { lang="zh-CN" } - -::: en -::: warning -It is known that PikPak Share can only see 40%-50% -::: -::: zh-CN ::: warning 已知目前pikpak分享只能看40%-50% ::: -::: en -You only need to fill in **`Username`, `Password`, `Shared ID`** three items, **root folder ID** can be written or not, if not written, the default is the root directory (root directory) - -- Root folder ID: If it is a multi-layer directory, which directory do you want to display as the root directory, you can write which root directory.(Refer to the method of obtaining the root folder ID below) -- Sharing password: if there is a password to share, write it, if not, don’t write it - -::: - -::: zh-CN 只需要填写 `用户名` ,`密码`,`分享ID` 三项即可 ,**根文件夹ID** 可写可不写,不写默认为root目录(根目录) - 根文件夹ID:如果是多层目录,你想让哪个目录展示当根目录你就写哪个根目录.(参考下方获取根文件夹ID方法) - 分享密码:分享的有密码就写,没有就不写 -::: - ![pikpak-share](/img/drivers/pikpak/pikpak-share.png) -### 获取根文件夹ID { lang="zh-CN" } - -### Get Root Folder ID { lang="en" } +### 获取根文件夹ID ![pikpak-share-rootfolder](/img/drivers/pikpak/pikpak-share-rootfolder.png) -::: en -**The root directory ID of the current pikpak share can no longer be obtained in the address, you need to check the data returned by the interface** - -- Open the F12 console and go to the Network tab -- Refresh the page and search for `detail`, find the last `detail` request -- Select `detail` and find `files` in the Response -- Find the corresponding folder name and check its `id` field content as the root folder ID.(You can confirm the corresponding folder through the `name` field) - -::: - -::: zh-CN **当前pikpak分享的根目录ID已经无法在地址中获取,需要查看接口返回的数据** - 打开F12控制台,进入网络(Network)选项卡 @@ -248,53 +100,20 @@ You only need to fill in **`Username`, `Password`, `Shared ID`** three items, ** - 选择`detail`,在响应(Response)中找到`files` - 找到对应的文件夹名称,查看其`id`字段内容即为根文件夹ID(可以通过`name`字段来确认对应的文件夹) -::: - -### Use transcoding address { lang="en" } - -### 使用转码地址 { lang="zh-CN" } - -::: en -Not enabled by default. When enabled, the download address will use the **transcoded address**, and you can get the **complete transcoded file** - -- After turning on the `Use transcoding address` option, you cannot use the `OpenList` web version to play the video, but you can **download it normally** or **use a third-party player** +### 使用转码地址 -::: -::: zh-CN 默认不启用,打开后 下载地址将使用**转码后的地址**,可获取 **完整的转码后的文件** - 打开 `使用转码地址` 选项后,无法使用 `OpenList` 网页版播放视频,但**可正常下载**或**使用第三方播放器** -::: - -### Batch add PikPak shared mounts { lang="en" } - -### 批量添加PikPak分享挂载 { lang="zh-CN" } +### 批量添加PikPak分享挂载 -::: en -software used:**https://github.com/yzbtdiy/alist_batch** -::: -::: zh-CN 使用的软件:**https://github.com/yzbtdiy/alist_batch** -::: -## Precautions { lang="en" } - -## 注意事项 { lang="zh-CN" } - -::: en -**Q**: Encountering verification code issues +## 注意事项 -**A**: - -- The method has now been adjusted to use `oauth2` for token refresh. -- The username and password are now only used for login to obtain the `Refresh token` and generate the `DeviceID`. -- When encountering the issue `Your operation is too frequent, please try again later`, please try logging in using **third-party authorization** (such as Google login) on the **official web version** or **official Android app**. Afterward, **obtain the `Refresh token` for mounting** — note the selection of the `Platform` at this time. - -::: -::: zh-CN **Q**:遇到验证码问题 **A**: @@ -303,105 +122,41 @@ software used:**https://github.com/yzbtdiy/alist_batch** - 账号、密码 现在仅用于登录来获取 `Refresh token` 以及 `DeviceID` 的生成 - 遇到 `Your operation is too frequent, please try again later` 问题时,请尝试**在 `官方网页版` 或 `官方安卓端APP` 使用第三方授权**(例如:谷歌授权登录)进行登录,之后**获取 `Refresh token` 进行挂载**————注意 此时`Platform`的选用 -::: - --- -::: en -**Q**: Encountering the following situation: `Failed load storage: failed init storage: Your operation is too frequent, please try again later` - -![pikpak-share](/img/drivers/pikpak/pikpak-error-1.png) - -**A**: This means that the access has been too frequent, and the account/IP will be unable to log in for a period of time. Note: After this occurs, **you can log in to the official client normally using third-party authorization**. Also, **using this IP to request any account may trigger the issue again**, so you could try logging in using the `Refresh token` method (though it is not guaranteed to be effective). -::: -::: zh-CN **Q**:出现下图情况:`Failed load storage: failed init storage: Your operation is too frequent, please try again later` ![pikpak-share](/img/drivers/pikpak/pikpak-error-1.png) **A**:说明访问过于频繁,该账号/IP将在一段时间内无法登录。注意:出现此情况后,**使用第三方授权可正常登录官方客户端**。同时,**使用该IP请求任何账号都有几率再次出现此问题**,可尝试使用`Refresh token`方式登录(不保证有效) -::: --- -::: en -**Q**: Encountering the `Click Here` prompt - -**A**: Please click on it, then open F12 or launch a packet-sniffing tool. Complete the CAPTCHA as shown in the image below, obtain the `captcha_token`, and enter it into the driver's `Captcha token` field. After saving, the driver should work normally — **this applies to cases where login is done with username and password**. - -![pikpak-share](/img/drivers/pikpak/pikpak-error-2.png) -::: -::: zh-CN **Q**:出现 `Click Here` 提示 **A**:请点击进入,然后先打开F12或启动抓包软件,然后完成滑动验证码,如下图所示,获取`captcha_token`,填入驱动的`Captcha token`字段后,保存,此时驱动应该正常工作——**适用于使用账号密码登录的情况** ![pikpak-share](/img/drivers/pikpak/pikpak-error-2.png) -::: --- -::: en -::: -::: zh-CN **Q**:出现下面的报错:`invalid refresh token for it may be has been refreshed by other process, more info redis: nil` **A**:情况一: 则表明 `Refresh token` 无效,请重新获取;情况二:`Platform`:选择错误,请更换选项 -::: --- -::: en -**Q**: Prompt when adding storage: **Failed init storage: invalid_account_or_password** What should I do, the password I entered is correct - -**A**: If the account password is not filled in incorrectly, it may be that you used Google, FB and other third-party quick registration when you registered. Although it seems that the account is a Google mailbox, you cannot log in with the mailbox, but you must use the first Three-party verification, **OpenList** does not support this kind of jumping to third-party verification, **so you need to bind an email address in the account settings and set a login password**, or register a new account -::: -::: zh-CN **Q**:添加存储时提示:**Failed init storage: invalid_account_or_password** 怎么办,我输入的密码的对的 **A**:如果不是账号密码填错,可能是注册的时候使用了Google,FB等第三方快捷注册,虽然看起来账号是谷歌邮箱,但实际上是不能用邮箱登入,而必须使用第三方验证,**OpenList** 现在还不支持这种跳转到第三方的验证,**所以你要在账号设置里绑定一个邮箱同时设置一下登录密码**,或者重新注册一个新账号 -::: --- -::: en -**Q**: Prompt when adding mount: **failed get objs: failed to list objs: Sorry, sharing is not available in the current region** - -**A**: Because access to PikPak is prohibited in China, just use a proxy for `OpenList`, how to make `OpenList` use a proxy [**One of the reference solutions, this method is limited to Windows build**](https://anwen-anyi.github.io/index/07-wenti.html#_41-alist%E5%A6%82%E4%BD%95-%E4%BD%BF%E7%94%A8-%E5%90%83%E5%88%B0-%E4%BB%A3%E7%90%86-proxy) -::: -::: zh-CN **Q**:添加挂载时提示:**failed get objs: failed to list objs: Sorry, sharing is not available in the current region** **A**:因为在国内PikPak是禁止访问的,给`OpenList`使用代理即可,如何让`OpenList`使用代理[**参考方案之一,此方法仅限于Windows搭建**](https://anwen-anyi.github.io/index/07-wenti.html#_41-alist%E5%A6%82%E4%BD%95-%E4%BD%BF%E7%94%A8-%E5%90%83%E5%88%B0-%E4%BB%A3%E7%90%86-proxy) -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -421,5 +176,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/quark.md b/pages/guide/drivers/quark.md index fc4ec92d5..b5aafceaa 100644 --- a/pages/guide/drivers/quark.md +++ b/pages/guide/drivers/quark.md @@ -1,100 +1,41 @@ --- -title: - en: Quark / TV / Open - zh-CN: 夸克网盘 / TV / Open -icon: iconfont icon-state -# This control sidebar order top: 684 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 夸克网盘 / TV / Open + https://pan.quark.cn -:::en -:::danger -Due to Quark Cloud Drive's speed limit issues, it can now only use local proxy for transfers. [See details here](https://github.com/alist-org/alist/issues/4318#issuecomment-1536214188) -::: - -:::zh-CN :::danger 由于夸克网盘限速问题,夸克网盘现在只能使用本地代理进行传输,[说明详见](https://github.com/alist-org/alist/issues/4318#issuecomment-1536214188) ::: -## 1. Quark { lang="en" } +## 1. 夸克网盘 -## 1. 夸克网盘 { lang="zh-CN" } +### Cookie -### Cookie { lang="en" } - -### Cookie { lang="zh-CN" } - -:::en -Press F12 to open "Developer Tools", select "Network", choose any request on the left, and find the one with the `Cookie` parameter. -::: -:::zh-CN 按F12打开“调试”,选中“网络”,随意在左侧选择请求,找到携带 `Cookie` 参数的就可以 -::: ![quark](/img/drivers/quark/quark_cookie.png) -### Root Folder ID { lang="en" } +### 根文件夹ID -### 根文件夹ID { lang="zh-CN" } - -:::en -The root directory ID is `0`. - -- For subfolder IDs, enter the folder and get the directory ID from the top address bar. The deeper the subdirectory, the further back the directory ID is in the address bar. To mount a specific subdirectory, just use its directory ID. - -::: -:::zh-CN 根目录ID为 `0` - 子文件夹ID进入文件夹后顶部地址栏获取目录ID,如果子目录越深目录ID在地址栏越靠后,想挂载哪个子目录就写那个子目录ID就可以 -::: - ![url](/img/drivers/quark/quark_fileid.png) -:::en -Note: Please use Chrome browser to obtain Cookies. If you use Firefox, you may remain as a guest and be prompted to log in. -::: -:::zh-CN 注意:请使用Chrome浏览器来获取Cookies,使用Firefox获取的Cookies或仍将停留在访客并请求登录。 -::: - -### [Online Preview/Download] is Slow? { lang="en" } - -### [在线预览/下载]很慢? { lang="zh-CN" } - -:::en -Quark Cloud Drive downloads are slow because a **membership** is required, and mounting can only (forcibly) use the `local proxy` method, which means your OpenList server needs to have high bandwidth. -- What is `local proxy`? - - `Local proxy` means your OpenList server acts as a relay: it first downloads to your OpenList server, then forwards to you. If your server's speed is not fast enough, the forwarding speed to you will also be slow. +### [在线预览/下载]很慢? -1. Use a server with higher bandwidth as a relay -2. Set up at home on your own computer -3. Or simply give up using it. - -::: -:::zh-CN 夸克云盘下载很慢是因为夸克云盘需要开通**会员**,并且挂载只能(强制)使用`本地代理`方式,就需要你搭建OpenList的机器有一个很高的带宽速度 - 什么是`本地代理`? @@ -104,32 +45,7 @@ Quark Cloud Drive downloads are slow because a **membership** is required, and m 2. 在自己家里电脑搭建 3. 直接放弃不用. -::: - -### Default Download Method { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -:::en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[Local Proxy]:::someclass==Forced===>a2[User Device] - classDef someclass fill:#f96 - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -:::zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -146,40 +62,13 @@ flowchart TB click c1 "../drivers/common.html#webdav-policy" ``` -::: - -:::en -Note: [**alist/issues/4318**](https://github.com/alist-org/alist/issues/4318#issuecomment-1536214188) -::: -:::zh-CN 说明:[**alist/issues/4318**](https://github.com/alist-org/alist/issues/4318#issuecomment-1536214188) -::: -## 2. Quark TV { lang="en" } +## 2. 夸克TV -## 2. 夸克TV { lang="zh-CN" } - -:::en -The TV version supports `302`, but only the `access` and `download` operations are supported; other operations are not supported (not available in the API). -::: -:::zh-CN TV 版支持 `302`,但是只能 `访问` 和 `下载` 两个操作,其它操作不支持(接口不支持) -::: - -### How to Add { lang="en" } - -### 添加方式 { lang="zh-CN" } -:::en - -1. Select the `QuarkTV` driver, fill in the mount path, and save. -2. Return to the drivers page, use the mobile app to scan the QR code (if the QR code is not displayed, click `Table Layout` in the upper right corner of the driver to switch from list mode to table mode). -3. After scanning and confirming, disable the driver, then enable the driver again to use it. - - `Refresh token`, `Device id`, and `Query token` will be filled in automatically, no manual input required. - - Please do not edit or modify them manually. - -::: -:::zh-CN +### 添加方式 1. 选择 `夸克TV / QuarkTV` 驱动,填写挂载路径,然后保存 2. 返回全部驱动页面,使用手机APP扫描二维码(如果不显示二维码,驱动右上角点击点击`表格布局` 从列表模式切换成表格模式) @@ -187,56 +76,17 @@ TV 版支持 `302`,但是只能 `访问` 和 `下载` 两个操作,其它操 - `Refresh token 刷新令牌`、`Device id 设备ID `、`Query token` 会自动填充,不需要人工填写 - 请不要进行编辑手动修改 -::: - ![](/img/drivers/tv_qrcode.png) -### Root Folder ID { lang="en" } - -### 根文件夹ID { lang="zh-CN" } - -:::en -The root directory ID is `0`. +### 根文件夹ID -- For subfolder IDs, enter the folder and get the directory ID from the top address bar. The deeper the subdirectory, the further back the directory ID is in the address bar. To mount a specific subdirectory, just use its directory ID. - -::: -:::zh-CN 根目录ID为 `0` - 子文件夹ID进入文件夹后顶部地址栏获取目录ID,如果子目录越深目录ID在地址栏越靠后,想挂载哪个子目录就写那个子目录ID就可以 -::: - ![url](/img/drivers/quark/quark_fileid.png) -### Default Download Method { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -:::en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|Default|a2[User Device] - classDef someclass fill:#f96 - c1[Local Proxy]-.Alternative.->a2[User Device] - b1[Proxy URL]-.Alternative.->a2[User Device] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -:::zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -257,70 +107,21 @@ flowchart TB click c1 "../drivers/common.html#webdav-policy" ``` -::: - -## 3. Quark Open { lang="en" } - -## 3. 夸克Open { lang="zh-CN" } +## 3. 夸克Open -:::en -:::danger -This "Open" is not an open interface in the true sense. - -No further tutorials are provided. -::: - -:::zh-CN :::danger 该“Open”并非真正意义上的开放接口。 不提供进一步的教程。 ::: -### Usage { lang="en" } - -### 使用方法 { lang="zh-CN" } - -:::en - -- Select `Quark Cloud Drive (OAuth2) Authentication Login` at [here](https://api.oplist.org). -- Fill in the AppID and SignKey you obtained to get the refresh token. -- Due to the lack of relevant documentation, please use the **online API** for refreshing. - -::: -:::zh-CN +### 使用方法 - 在[这里](https://api.oplist.org)选择`夸克网盘 (OAuth2) 验证登录`。 - 根据自己获取到的AppID、SignKey填写,获取刷新令牌。 - 因为缺少相关文档,请使用**在线api**进行刷新 -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -:::en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -:::zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -338,5 +139,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-policy" click c1 "../drivers/common.html#webdav-policy" ``` - -::: diff --git a/pages/guide/drivers/s3.md b/pages/guide/drivers/s3.md index 5df38eaca..d06b0f319 100644 --- a/pages/guide/drivers/s3.md +++ b/pages/guide/drivers/s3.md @@ -1,271 +1,108 @@ --- -title: - en: S3 - zh-CN: 对象存储(S3) -icon: iconfont icon-state -# This control sidebar order top: 895 -# A page can have multiple categories categories: - guide - drivers --- -::: en -S3 object storage protocols, such as COS, OSS, B2, etc. -::: -::: zh-CN -S3 对象存储协议,如 COS、OSS、B2。 -::: +# 对象存储(S3) -## Bucket { lang="en" } +S3 对象存储协议,如 COS、OSS、B2。 -## 存储桶 { lang="zh-CN" } +## 存储桶 -::: en -bucket name -::: -::: zh-CN 存储桶名 -::: - -## Endpoint { lang="en" } -## Endpoint { lang="zh-CN" } +## Endpoint -::: en -Endpoint address (If you don’t know, you can read the official documents below) -::: -::: zh-CN Endpoint address(不知道可以看下方的官方文档) -::: -## Region { lang="en" } +## 地区 -## 地区 { lang="zh-CN" } - -::: en -area (If you don’t know, you can read the official documents below) -::: -::: zh-CN 地区(不知道可以看下方的官方文档) -::: - -## Access key id { lang="en" } -## 访问密钥 id { lang="zh-CN" } +## 访问密钥 id -::: en Access key id -::: -::: zh-CN -Access key id -::: - -## Secret access key { lang="en" } -## 安全访问密钥 { lang="zh-CN" } +## 安全访问密钥 -::: en Secret access key -::: -::: zh-CN -Secret access key -::: - -## Session token { lang="en" } -## 会话令牌 { lang="zh-CN" } +## 会话令牌 -::: en -Session token,Normally, the three-segment token needs to be used, and the two-segment token does not need to fill in this option. -::: -::: zh-CN 会话令牌,三段式token需要使用,正常是两段式不需要填写 -::: -## Custom host { lang="en" } +## 自定义 Host -## 自定义 Host { lang="zh-CN" } - -::: en -Custom cdn acceleration domain name -::: -::: zh-CN 自定义 CDN 加速域名 -::: - -## Enable custom host presign { lang="en" } - -## 启用自定义 HOST 签名 { lang="zh-CN" } - -::: en -When "Custom host" is left blank, this option does not function. -When disabled: The returned URL does not include signature, which is suitable for cases where the custom host is a CDN. +## 启用自定义 HOST 签名 -When enabled: The returned URL includes signature, which is suitable for cases where the host and OSS are from the same data center, the Endpoint is an internal access domain within the data center, and the custom host is an external access domain. -::: -::: zh-CN 不填写“自定义 Host”时本选项无效。 关闭时:返回的URL后不接签名信息,适用于自定义Host是CDN的情况。 启用时:返回的URL后接签名信息,适用于主机和对象存储服务来自同一个机房,Endpoint是机房内部访问域名,自定义Host是外部访问域名的情况。 -::: -## Sign url expire { lang="en" } +## 签名链接有效期 -## 签名链接有效期 { lang="zh-CN" } - -::: en -The validity period of the signed download address is 4 hours by default. If a custom accelerated domain name is used, this option is invalid. -::: -::: zh-CN 签名下载地址的有效期默认为 4 小时。 如果使用自定义加速域名,该选项无效。 -::: - -## Placeholder { lang="en" } -## 占位文件名 { lang="zh-CN" } +## 占位文件名 -::: en -The name of placeholder file. -::: -::: zh-CN 占位符文件的名称。 -::: -## Force path style { lang="en" } +## 强制路径样式 -## 强制路径样式 { lang="zh-CN" } - -::: en -If enable ForcePathStyle, usually needed for minio. -::: -::: zh-CN 是否启用 `ForcePathStyle`,通常是 minio 需要的。 -::: - -## List object version { lang="en" } -## 列出对象版本 { lang="zh-CN" } +## 列出对象版本 -::: en -Refer to s3 sdk documents. -::: -::: zh-CN 参见 S3 的 SDK 文档。 -::: -## Remove bucket { lang="en" } +## 移除bucket -## 移除bucket { lang="zh-CN" } - -::: en -Remove bucket name from path when using custom host. -::: -::: zh-CN 使用自定义主机时从路径中删除bucket名称。 -::: - -## Add filename to disposition { lang="en" } -## 添加filename到disposition { lang="zh-CN" } +## 添加filename到disposition -::: en -Add filename to Content-Disposition header. -::: -::: zh-CN 添加filename到Content-Disposition头中。 -::: -## Enable direct upload { lang="en" } +## 启用前端直传 -## 启用前端直传 { lang="zh-CN" } - -::: en -Enable direct front-end upload to S3. -::: -::: zh-CN 启用前端直传。 -::: - -## Direct upload host { lang="en" } -## 直传主机 { lang="zh-CN" } +## 直传主机 -::: en -The host for direct upload. -::: -::: zh-CN 直传主机地址。 -::: -## User agent { lang="en" } +## 自定义 User-Agent -## 自定义 User-Agent { lang="zh-CN" } - -::: en -Custom User-Agent for S3 requests. Leave empty to use the default generated by AWS SDK for Go (e.g. `aws-sdk-go/1.55.8 (go1.xx.x; ; )`). -::: -::: zh-CN 自定义 S3 请求的 User-Agent。留空默认为 AWS SDK for Go 自动生成(如:`aws-sdk-go/1.55.8 (go1.xx.x; ; )`)。 -::: - -## Add object storage examples and official documents { lang="en" } -## 添加对象存储示例及官方文档 { lang="zh-CN" } +## 添加对象存储示例及官方文档 -::: en -`S3 Object Storage` includes but is not limited to these. There are many more, such as Microsoft and Google, which have not been added. There are no examples here because it is difficult to register and activate and cannot provide example tutorials. If you have an account, please contact us and provide an account and I will add it. -::: -::: zh-CN `对象存储`包含但不限于只有这些,还有很多例如微软谷歌没有添加,这里没有示例是因为注册开通困难无法提供示例教程,如果您有帐号欢迎联系提供帐号我来添加 -::: -### Alibaba { lang="en" } - -### 阿里云 { lang="zh-CN" } - -::: en -[**Alibaba Cloud OSS Official Website**](https://oss.console.aliyun.com/) - -[**Comparison Table of Endpoints in Different Regions of OSS under Public Cloud**](https://help.aliyun.com/document_detail/31837.html) -::: -::: en -::: tip -OpenList needs to skip `referer` to mount **Alibaba Cloud Disk**. If you do not allow `referer` to be empty in anti-leech, it will not work, because Alibaba Cloud Disk uses Alibaba Cloud OSS (in Beijing area) +### 阿里云 -In fact, the `Region|Region` option can be written as `Endpoint`, but for the sake of specification, let’s write it according to the comparison table.::: -::: -::: zh-CN [**阿里云OSS官网**](https://oss.console.aliyun.com/) [**公共云下OSS各地域Endpoint对照表**](https://help.aliyun.com/document_detail/31837.html) -::: -::: zh-CN + ::: tip OpenList 挂载**阿里云盘**需要跳过 `referer` ,若你在防盗链开启不允许 `referer` 为空的话会无法使用,因为阿里云盘用的就是阿里云OSS(北京地区的) 其实 `地区|Region` 选项写 `Endpoint` 一样的也可以用,但是为了规范还是照着对照表写吧。 ::: -::: en -Fill in example: -![s3](/img/drivers/s3/al.png) -::: -::: zh-CN 填写示例: ![s3](/img/drivers/s3/al.png) -::: -### Tencent { lang="en" } +### 腾讯云 -### 腾讯云 { lang="zh-CN" } - -::: zh-CN [**腾讯云COS官网**](https://console.cloud.tencent.com/cos) [**公共云下COS各地域Endpoint对照表**](https://cloud.tencent.com/document/product/436/6224) @@ -273,22 +110,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/tx.png) -::: -::: en -[**Tencent Cloud COS Official Website**](https://console.cloud.tencent.com/cos) -[**Collection Table of Endpoints in Each Region of COS under the Public Cloud**](https://cloud.tencent.com/document/product/436/6224) +### 火山引擎 -Fill in example: - -![s3](/img/drivers/s3/tx.png) -::: - -### VolcEngine { lang="en" } - -### 火山引擎 { lang="zh-CN" } - -::: zh-CN [**火山引擎TOS官网**](https://console.volcengine.com/tos) [**公共云下TOS各地域Endpoint对照表**](https://www.volcengine.com/docs/6349/107356) @@ -296,22 +120,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/zijie_tos.png) -::: -::: en -[**VolcEngine TOS Official Website**](https://console.volcengine.com/tos) - -[**Collection Table of Endpoints in Each Region of COS under the Public Cloud**](https://www.volcengine.com/docs/6349/107356) -Fill in example: +### 天翼云 -![s3](/img/drivers/s3/zijie_tos.png) -::: - -### Tianyi { lang="en" } - -### 天翼云 { lang="zh-CN" } - -::: zh-CN [**天翼云OOS官网**](https://oos-cn.ctyun.cn/oos/ctyun/consoleBucket.html) [**公共云下OOS各地域Endpoint对照表**](https://www.ctyun.cn/document/10026693/10027878) @@ -319,22 +130,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/ty.png) -::: -::: en -[**Tianyi Cloud OOS Official Website**](https://oos-cn.ctyun.cn/oos/ctyun/consoleBucket.html) - -[**Comparison Table of Endpoints of OOS Regions under Public Cloud**](https://www.ctyun.cn/document/10026693/10027878) - -Fill in example: - -![s3](/img/drivers/s3/ty.png) -::: - -### Baidu { lang="en" } -### 百度云 { lang="zh-CN" } +### 百度云 -::: zh-CN [**百度云BOS官网**](https://console.bce.baidu.com/bos) [**自行配置Bucket的Referer白名单权限**](https://cloud.baidu.com/doc/BOS/s/Bk6kqu8eq#设置referer白名单) @@ -346,26 +144,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/bd.png) -::: -::: en -[**Baidu Cloud BOS official website**](https://console.bce.baidu.com/bos) - -[**Self-configure Bucket’s Referer whitelist permissions**](https://cloud.baidu.com/doc/BOS/s/Bk6kqu8eq#Setrefererwhitelist) - -[**comparison table of Region and Endpoint of BOS under public cloud**](https://intl.cloud.baidu.com/doc/BOS/s/xjwvyq9l4-en) - -**Endpoint comparison table compatible with AWS S3**[**Click to view details**](https://cloud.baidu.com/doc/BOS/s/xjwvyq9l4) -Fill in example: +### 七牛云 -![s3](/img/drivers/s3/bd.png) -::: - -### Qiniu { lang="en" } - -### 七牛云 { lang="zh-CN" } - -::: zh-CN [**七牛云Kodo官网**](https://portal.qiniu.com/kodo/bucket) [**公共云下Kodo各地域Region和Endpoint对照表**](https://developer.qiniu.com/kodo/4088/s3-access-domainname) @@ -373,22 +154,8 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/qn.png) -::: -::: en -[**Qiniu Cloud Kodo official website**](https://portal.qiniu.com/kodo/bucket) - -[**Region and Endpoint comparison table of Kodo under the public cloud**](https://developer.qiniu.com/kodo/4088/s3-access-domainname) - -Fill in example: - -![s3](/img/drivers/s3/qn.png) -::: - -### Huawei { lang="en" } -### 华为云 { lang="zh-CN" } - -::: zh-CN +### 华为云 [**华为云OBS官网**](https://console.huaweicloud.com/console/#/obs/manager/buckets) @@ -399,22 +166,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/hw.png) -::: -::: en -[**HUAWEI CLOUD OBS Official Website**](https://www.huaweicloud.com/intl/en-us/) - -[**Check the Region and Endpoint comparison table of OBS under the public cloud**](https://developer.huaweicloud.com/intl/en-us/endpoint), the secret key AK/SK[acquire](https://console.huaweicloud.com/iam/?region=cn-north-4#/mine/accessKey) to set by yourself. - -Fill in example: - -![s3](/img/drivers/s3/hw.png) -::: - -### JD { lang="en" } -### 京东云 { lang="zh-CN" } +### 京东云 -::: zh-CN [**京东云OSS官网**](https://www.jdcloud.com/cn/products/object-storage-service) 优点:[免费10G](https://www.jdcloud.com/cn/activity/oss),支持IPV6 @@ -426,26 +180,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/jd.png) -::: -::: en -[**JD CLOUD OBS Official Website**](https://www.jdcloud.com/cn/products/object-storage-service) - -Advantages: [free 10G](https://www.jdcloud.com/cn/activity/oss), support IPV6 - -Disadvantages: need to precharge 100 to open,However, after the pre-deposit is completed, the object storage is activated, and the pre-deposited amount can still be used after withdrawal. - -[**Region and Endpoint comparison table of Kodo under the public cloud**](https://docs.jdcloud.com/cn/object-storage-service/oss-endpont-list) - -Fill in example: - -![s3](/img/drivers/s3/jd.png) -::: -### Doge { lang="en" } +### 多吉云 -### 多吉云 { lang="zh-CN" } - -::: zh-CN [**多吉云OSS官网**](https://www.dogecloud.com/product/oss),如果要添加多吉云,请使用单独的`多吉云`驱动添加,请勿使用`对象存储`驱动 地区代码: @@ -455,24 +192,9 @@ Fill in example: ``` ![s3](/img/drivers/s3/doge.png) -::: -::: en -[**Doge Cloud OSS Official Website**](https://www.dogecloud.com/product/oss),If you want to add Doge, use a separate `DogeCloud` driver to add, please do not use the `S3` the driver - -Region Code: - -```json -// 0: 上海(华东),1: 北京(华北),2: 广州(华南),3: 成都(西南) -``` - -![s3](/img/drivers/s3/doge.png) -::: - -### 139 { lang="en" } -### 移动云 { lang="zh-CN" } +### 移动云 -::: zh-CN [**移动云EOS官网**](https://console.ecloud.10086.cn/api/page/eos-console-web/CIDC-RP-00/eos/bucket?productType=os&v=new) [**公共云下OSS各地域Region和Endpoint对照表**](https://ecloud.10086.cn/op-help-center/doc/article/48082) @@ -480,33 +202,13 @@ Region Code: 填写示例: ![s3](/img/drivers/s3/139.png) -::: -::: en -[**139 CLOUD EOS Official Website**](https://console.ecloud.10086.cn/api/page/eos-console-web/CIDC-RP-00/eos/bucket?productType=os&v=new) - -[**Region and Endpoint comparison table of Kodo under the public cloud**](https://ecloud.10086.cn/op-help-center/doc/article/48082) -Fill in example: +### 又拍云 -![s3](/img/drivers/s3/139.png) -::: - -### upyun { lang="en" } - -### 又拍云 { lang="zh-CN" } - -::: zh-CN [**又拍云USS官网**](https://console.upyun.com/services/file/):[点击这里进行配置USS](./uss.md) -::: -::: en -[**Yupyun USS Official Website**](https://console.upyun.com/services/file/):[Click here to configure USS](./uss.md) -::: - -### Amazon { lang="en" } -### 亚马逊 { lang="zh-CN" } +### 亚马逊 -::: zh-CN [**亚马逊S3官网**](https://s3.console.aws.amazon.com/s3/) 端点 URL 包含存储桶所在的区域名称 @@ -531,40 +233,8 @@ Region (地区)参数:**`us-east-2`** ![s3](/img/drivers/s3/aws.png) -::: - -::: en -[**Amazon S3 Official Website**](https://s3.console.aws.amazon.com/s3/) - -The endpoint URL contains the name of the region where the bucket is located - -Example 1, if your bucket is in the US East (Ohio) region - -Then the format of the Endpoint URL is **`s3.us-east-2.amazonaws.com/your-bucket-name`** - -Region parameter: **`us-east-2`** - -Bucket name: **`your-bucket-name`** (your own bucket name) - -Example 2, my storage bucket is `Tokyo`, and the name of the storage bucket is `cdn-aw`, then the complete parameter filling is as follows - -1. **Endpoint**: s3.ap-northeast-1.amazonaws.com -2. **Region**: ap-northeast-1 -3. **Bucket Name**: cdn-aw - -Let's take a look at the complete parameter filling example - -Fill in example: - -![s3](/img/drivers/s3/aws.png) - -::: - -### Cloudflare R2 { lang="en" } +### Cloudflare R2 -### Cloudflare R2 { lang="zh-CN" } - -::: zh-CN [**Cloudflare R2官网**](https://dash.cloudflare.com/) **Endpoint**:S3 端点可通过 **`https://.r2.cloudflarestorage.com`** 端点使用 @@ -574,24 +244,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/cfr2.png) -::: -::: en -[**Cloudflare R2 Official Website**](https://dash.cloudflare.com/) - -**Endpoint**: The S3 endpoint is available via the **`https://.r2.cloudflarestorage.com`** endpoint. - -**`ACCOUNT_ID`**: This account ID can be seen everywhere, and the simplest is the position at the top of the browser. The string of IDs behind the website link is your account ID. - -填写示例: - -![s3](/img/drivers/s3/cfr2.png) -::: - -### Backblaze B2 { lang="en" } -### Backblaze B2 { lang="zh-CN" } +### Backblaze B2 -::: zh-CN [**Backblaze官网**](https://secure.backblaze.com/b2_buckets.htm) 全称 **`Backblaze`**,不需要验证信息,说是前10G免费。 @@ -599,22 +254,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/b2.png) -::: -::: en -[**B2 Official Website**](https://secure.backblaze.com/b2_buckets.htm) - -The full name is **`Backblaze`**, no verification information is required, and it is said that the first 10G is free. - -Fill in example: - -![s3](/img/drivers/s3/b2.png) -::: -### Scaleway { lang="en" } +### Scaleway -### Scaleway { lang="zh-CN" } - -::: zh-CN [**Scaleway官网**](https://console.scaleway.com/object-storage/buckets) 需要卡验证信息 @@ -624,24 +266,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/sc.png) -::: -::: en -[**Scaleway Official Website**](https://console.scaleway.com/object-storage/buckets) - -Card required, verification information - -[**Region and Endpoint comparison table of Kodo under the public cloud**](https://www.scaleway.com/en/docs/storage/object/concepts/#object-storage) - -Fill in example: - -![s3](/img/drivers/s3/sc.png) -::: -### Minio { lang="en" } +### Minio -### Minio { lang="zh-CN" } - -::: zh-CN 使用`Minio`自建对象存储的应该都知道,ID和秘钥如何获取,这里就不说了 只需要填写存储桶名称,访问秘钥ID,安全访问秘钥,Endpoint 这四个选项,地区空着即可 @@ -653,47 +280,9 @@ Fill in example: 填写示例: ![s3](/img/drivers/s3/mini-io.png) -::: -::: en -Those who use `Minio` self-built object storage should know how to obtain the ID and secret key, so I won’t talk about it here. - -You only need to fill in the four options of bucket name, access key ID, security access key, and Endpoint, and leave the region empty. - -**Endpoint:`http(s)://your-server:9001/`**, You can use IP or domain name - -Finally, remember to enable **`Force Path Style`** to use, otherwise it cannot be used [Click to view details](https://github.com/alist-org/alist/discussions/4920#discussioncomment-6635452) - -Fill in example: - -![s3](/img/drivers/s3/mini-io.png) -::: - -### Google Cloud { lang="en" } - -::: en -I assume you already have a Google Cloud account, so let’s go directly to the Google Cloud Storage settings page: - -Then you will enter the following page: - -![Google Cloud](/img/drivers/s3/googleStorage1.png) - -You can see Endpoint and Access Key on this page, you need to select an account and create an Access Key - -![Google Cloud](/img/drivers/s3/googleStorage2.png) - -Access Key/Secret will be displayed on the new page, They will only be displayed this time. Please save them properly. - -![Google Cloud](/img/drivers/s3/googleStorage3.png) - -At this point, the EndPoint, access key Id, and security access key required by OpenList have been obtained. Please view the remaining buckets and regions in the corresponding buckets. - -![Google Cloud](/img/drivers/s3/googleStorage4.png) - -::: -### Google Cloud { lang="zh-CN" } +### Google Cloud -::: zh-CN 默认你已经有Google Cloud的账号了,那么我们直接进入 Google Cloud Storage的设置页面: 然后会进入下面这个页面 @@ -712,39 +301,8 @@ At this point, the EndPoint, access key Id, and security access key required by ![Google Cloud](/img/drivers/s3/googleStorage4.png) -::: - -### Intranet access { lang="en" } - -### 内网接入 { lang="zh-CN" } - -::: en -The server and object storage need to be in the same region, currently only supports `Alibaba Cloud OSS`. For example, if you are `Alibaba Cloud Hangzhou Server` and `Alibaba OSS in Hangzhou`, you can use it. - -- Previous behavior (taking Aliyun OSS as an example): - - Download link without custom host (using internal endpoint): - - ``` - https://.oss-cn-beijing-internal.aliyuncs.com/?X-Amz-Algorithm=***&X-Amz-Credential=***... - ``` - - - Download link within custom host (using external endpoint, the following link is **invalid**): - - ``` - https://.oss-cn-beijing.aliyuncs.com/ - ``` - -- Current behavior when `EnableCustomHostPresign` is `false`: Same as previous - -- Current behavior when `EnableCustomHostPresign` is `true` : - - Download link within custom host (using external endpoint, the following link is **valid**): +### 内网接入 - ``` - https://.oss-cn-beijing.aliyuncs.com/?X-Amz-Algorithm=***&X-Amz-Credential=***... - ``` - -::: -::: zh-CN 服务器和对象存储需要同一地域,目前仅支持`阿里云OSS`。例如你是`阿里云杭州服务器` 和 `杭州地区的对象存储` 就可以使用。 - 之前的行为(以阿里云 OSS 为例): @@ -769,54 +327,14 @@ The server and object storage need to be in the same region, currently only supp https://.oss-cn-beijing.aliyuncs.com/?X-Amz-Algorithm=***&X-Amz-Credential=***... ``` -::: - -### How to solve the problem of files with the same name as the folder { lang="en" } - -### 出现和文件夹同名文件如何解决 { lang="zh-CN" } +### 出现和文件夹同名文件如何解决 -::: en -After mounting the object storage and opening the folder, there is always a file with the same name as the folder that cannot be deleted or opened - -- The solution is to change the `List object version` in the configuration to V2, as shown in the following figure - ![](/img/drivers/s3/v1andv2.png) - -::: -::: zh-CN 挂载对象存储后打开文件夹后总是会出现一个和文件夹名一样的文件无法删除也无法打开 - 解决办法把配置里面的 `列出对象版本` 换成V2就可以,参考如下图所示 ![](/img/drivers/s3/v1andv2.png) -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -836,5 +354,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/seafile.md b/pages/guide/drivers/seafile.md index 8dc45658d..dbff68bea 100644 --- a/pages/guide/drivers/seafile.md +++ b/pages/guide/drivers/seafile.md @@ -1,142 +1,49 @@ --- -title: - en: Seafile - zh-CN: Seafile -icon: iconfont icon-state -# This control sidebar order top: 495 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -Seafile uses libraries as a unit for version control and synchronization. +# Seafile -Use this driver to mount a Seafile repository to OpenList, encrypted libraries are supported. -::: -::: zh-CN Seafile 是以资料库为单位进行版本控制和同步的开源云盘。 使用本驱动可以挂载 Seafile 的资料库到 OpenList,已支持访问加密资料库。 -::: -## Root folder path { lang="en" } - -## 根文件夹ID { lang="zh-CN" } - -::: en - -1. If the `RepoId` is not set, this configuration specifies the name and sub-path of the library to access, defaulting to the root path: `/`, indicating displaying all libraries. Suppose you have a library named `example`, if you only want to mount the contents of this library, you can set this item to `/example`; if you only want to display a directory named `abc` within this library, you can set this item to `/example/abc`, and so on. -2. If the `RepoId` is set, this configuration is used to set the directory within the specified library, defaulting to the root directory: `/`. - -::: - -::: zh-CN +## 根文件夹ID 1. 如果未设置资料库ID(RepoId),则该配置可指定要访问的资料库的名称与子路径,默认为根路径:`/`,表示展示所有资料库。假设你有一个资料库名为 `example`,如果你只想挂载该资料库的内容,可以将本项设为 `/example`;如果你只想展示该资料库内一个名为 `abc` 的目录,则可以将本项设置为 `/example/abc` ,以此类推。 2. 如果设置了资料库ID(RepoId),则该配置用来设置指定资料库内的目录,默认为根目录:`/`。 -::: - -## Address { lang="en" } - -## 地址 { lang="zh-CN" } - -::: en -Your Seafile server address, e.g. - -- `https://seacloud.cc` -- `http://192.168.1.24:8000` +## 地址 -::: - -::: zh-CN 你的 Seafile 服务器地址,形如: - `https://seacloud.cc` - `http://192.168.1.24:8000` -::: - -## Username { lang="en" } - -## 用户名 { lang="zh-CN" } +## 用户名 -::: en -The email or username used to log in to your Seafile server. -::: -::: zh-CN 用于登录你的 Seafile 服务器的邮箱或用户名。 -::: -## Password { lang="en" } +## 密码 -## 密码 { lang="zh-CN" } - -::: en -The password for your email or username. -::: -::: zh-CN 邮箱或用户名对应的密码。 -::: - -## Token { lang="en" } -## 令牌 { lang="zh-CN" } +## 令牌 -::: en -Applicable to certain Seafile cloud storage systems integrated with enterprise/school unified authentication. For such cloud storage systems, there is no need to enter the username and password; only the token information needs to be filled in. Taking Tsinghua Cloud Disk as an example, the following steps can be used to obtain the token: `cookie` → `seahub_auth` → `"student ID@tsinghua.edu.cn@random string"`, where the random string is the token. - -::: -::: zh-CN 适用于某些接入了企业/学校统一认证的Seafile云盘。对于这类云盘,无需填写用户名和密码,只需要填写令牌信息即可。以清华云盘为例,可采用如下步骤获取令牌:`cookie`-->`seahub_auth`-->`"学号@tsinghua.edu.cn@随机字符串"`,这里的随机字符串即为令牌。 -::: - -## RepoId (Library ID) { lang="en" } - -## RepoId(资料库ID) { lang="zh-CN" } - -::: en -Seafile library ID, such as: `86fc6316-3f2a-4c64-b6f5-d4d2b341bc9a` - -- Open the Seafile library in a browser, and you can see the library ID in the address bar. -- Can be left empty, if left empty, all visible libraries for the specified account will be displayed. +## RepoId(资料库ID) -::: -::: zh-CN Seafile 资料库ID,形如:`86fc6316-3f2a-4c64-b6f5-d4d2b341bc9a` - 在浏览器中打开Seafile资料库,即可在地址栏中看到资料库ID。 - 可留空不填,不填则展示指定账号所有可见的资料库。 -::: +## RepoPwd(资料库密码) -## RepoPwd (Library Password) { lang="en" } - -## RepoPwd(资料库密码) { lang="zh-CN" } - -::: en -Set the password used to access encrypted libraries, leave blank if no encrypted libraries are present. - -After configuring this item, OpenList will use this password to access all encrypted libraries for the specified account, without affecting access to unencrypted libraries. If you have multiple encrypted libraries using the same password, they can all be accessed normally. - -Different passwords for different libraries within a storage are not currently supported. If you have such a requirement, you can add multiple Seafile storage to achieve this, allowing you to specify different passwords for accessing different encrypted libraries. - -If you still cannot access files within encrypted libraries after setting the library password, please check if the password and other configurations above are correct. It is recommended to restart OpenList after modifying this password configuration to avoid caching issues. -::: -::: zh-CN 设置用来访问加密资料库的密码,如果没有加密资料库请留空。 配置该项后 OpenList 将会用此密码访问指定账号所有已加密的资料库,不会影响未加密资料库的访问。如果你有多个加密资料库使用了相同的密码则均可以正常访问。 @@ -144,35 +51,8 @@ If you still cannot access files within encrypted libraries after setting the li 暂不支持在一个存储内对不同的资料库配置不同的密码进行访问,如果你有这种需求,可以添加多个 Seafile 存储来实现,这样就可以对不同的加密资料库指定不同的密码进行访问。 如果设置了资料库密码后仍无法访问加密资料库内的文件,请检查密码及上面的其他配置是否有误,建议修改此密码配置后重新启动 OpenList,避免缓存的影响。 -::: - -### The default download method used { lang="en" } -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -192,5 +72,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/sftp.md b/pages/guide/drivers/sftp.md index e97548054..cd883b40d 100644 --- a/pages/guide/drivers/sftp.md +++ b/pages/guide/drivers/sftp.md @@ -1,25 +1,12 @@ --- -title: - en: SFTP - zh-CN: SFTP -icon: iconfont icon-state -# This control sidebar order top: 897 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# SFTP + 即通过 SSH 协议访问文件。 ## 地址 diff --git a/pages/guide/drivers/smb.md b/pages/guide/drivers/smb.md index 75c693bc9..73fa1e8a5 100644 --- a/pages/guide/drivers/smb.md +++ b/pages/guide/drivers/smb.md @@ -1,63 +1,18 @@ --- -title: - en: SMB - zh-CN: SMB -icon: iconfont icon-state -# This control sidebar order top: 896 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -It is to mount smb to OpenList, instead of sharing OpenList through smb, instead of sharing smb like webdav. -Because there are no related equipment and services, it is impossible to test. You can test it yourself according to the method provided in the **`GitHub Discussions`** discussion -**https://github.com/alist-org/alist/discussions/2215#discussioncomment-4104018** -Wait for the more detailed tutorials to be supplemented after the relevant equipment or services are available, or wait for the PR of the boss who has no problem with the test. -::: -::: zh-CN +# SMB + 是把smb挂载到OpenList,而不是OpenList通过smb共享出来,而不是smb能像webdav共享出来。 因无相关设备和服务,无法测试,可以根据 **GitHub Discussions** 讨论中提供的办法自行测试一下 **https://github.com/alist-org/alist/discussions/2215#discussioncomment-4104018** 等待有了相关设备或服务后更详情的教程再进行补充,或者等待测试没问题的大佬pr。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -75,5 +30,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/strm.md b/pages/guide/drivers/strm.md index eab3bceda..3ba57d36c 100644 --- a/pages/guide/drivers/strm.md +++ b/pages/guide/drivers/strm.md @@ -1,22 +1,10 @@ --- -title: - en: Strm - zh-CN: Strm -icon: iconfont icon-state -# This control sidebar order top: 990 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# Strm + diff --git a/pages/guide/drivers/strm_text.md b/pages/guide/drivers/strm_text.md index b66c54755..d9c7c6a56 100644 --- a/pages/guide/drivers/strm_text.md +++ b/pages/guide/drivers/strm_text.md @@ -1,17 +1,3 @@ -::: en -**Strm** is a driver that allows you to convert supported files from multiple cloud drives into .strm files. - -::: warning Important Notes -Please understand the function of strm files before use - -Only the **`Download Preview (Read-Only)`** operation is supported. Other operations such as **Copy, Delete, Rename, -Offline Download, Upload** are **not supported**. - -Strm uses a local proxy method, and during the **`Download Preview (Read-Only)`** operation, it will consume traffic -from the deployed machine (depending on the number of files; individual files typically consume less than 100KB). -::: - -::: zh-CN **Strm** 是一种可以将多个不同云盘中的支持的文件转换为strm文件的驱动。 ::: warning 注意事项 @@ -22,25 +8,9 @@ from the deployed machine (depending on the number of files; individual files ty Strm采用本地代理方式,在 **`下载预览(只读)`** 操作时会消耗一定部署机器的流量(取决于文件数量、单个文件一般不超过100KB) ::: -### Parameter Descriptions { lang="en" } - -### 参数介绍 { lang="zh-CN" } - -#### Path { lang="en" } - -#### 路径 { lang="zh-CN" } - -::: en -The full path in OpenList where .strm files should be generated. -Examples: - -``` -/115_open/Video -/kuake_open/Video -``` +### 参数介绍 -::: -::: zh-CN +#### 路径 需要生成strm文件的`OpenList上的完整路径`。 @@ -51,25 +21,8 @@ Examples: /kuake_open/Video ``` -::: - -#### Site URL { lang="en" } - -#### 站点URL { lang="zh-CN" } +#### 站点URL -::: en -The URL prefix for the generated .strm files. - -For example, if the directory /115_open/Video contains the file:`/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.mkv` - -And you enter http://localhost:5244 as the Site URL, the generated .strm file will point to: -`http://localhost:5244/d/115_open/Video/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.strm` - -This field is optional. If left blank, the current access URL will be used as the default prefix. - -::: - -::: zh-CN Strm文件的URL前缀。 如在`/115_open/Video`目录下有`/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.mkv` @@ -79,30 +32,8 @@ Strm文件的URL前缀。 此项可默认不填、不填时URL前缀会采用你当前访问的地址作为URL前缀 -::: - -#### File Type Filter { lang="en" } - -#### 过滤文件类型 { lang="zh-CN" } - -::: en -Specify which file types should be included for .strm generation. - -The following types are built-in: - -``` -Video Type -mp4,mkv,flv,avi,wmv,ts,rmv,web - -Audio Type -mp3,flac,aac,wav,ogg,m4a,wma,alac -``` +#### 过滤文件类型 -You can add more types as needed. Use **commas (,)** to separate multiple file types. - -::: - -::: zh-CN 过滤需要生成的strm的文件类型 目前内置了如下文件类型 @@ -117,30 +48,8 @@ mp3,flac,aac,wav,ogg,m4a,wma,alac 可以自行补充,多文件类型间采用 **,** 分割 -::: - -#### Encode Path { lang="en" } - -#### 编码路径 { lang="zh-CN" } +#### 编码路径 -::: en -Whether to enable URL path encoding. - -If disabled, the .strm URL will be: - -``` -http://localhost:5244/d/Video/电影/再见,李可乐 (2023)/再见,李可乐 (2023) - 2160p.strm -``` - -If enabled, it will be: - -``` -http://localhost:5244/d/Video/%E7%94%B5%E5%BD%B1/%E5%86%8D%E8%A7%81%EF%BC%8C%E6%9D%8E%E5%8F%AF%E4%B9%90%20(2023)/%E5%86%8D%E8%A7%81%EF%BC%8C%E6%9D%8E%E5%8F%AF%E4%B9%90%20(2023)%20-%202160p.mkv -``` - -::: - -::: zh-CN 是否启用路径编码 不启用时生成的strm文件为: @@ -155,79 +64,25 @@ http://localhost:5244/d/Video/电影/再见,李可乐 (2023)/再见,李可 http://localhost:5244/d/Video/%E7%94%B5%E5%BD%B1/%E5%86%8D%E8%A7%81%EF%BC%8C%E6%9D%8E%E5%8F%AF%E4%B9%90%20(2023)/%E5%86%8D%E8%A7%81%EF%BC%8C%E6%9D%8E%E5%8F%AF%E4%B9%90%20(2023)%20-%202160p.mkv ``` -::: - -#### Without Url { lang="en" } - -#### 不包含URL前缀 { lang="zh-CN" } - -::: en -The generated strm file will not contain URL prefixes +#### 不包含URL前缀 -::: - -::: zh-CN 开启后生成的strm文件将不包含URL前缀 -::: - -#### SaveStrmToLocal { lang="en" } - -#### 保存Strm文件至本地 { lang="zh-CN" } - -::: en -When enabled, accessing a directory within or mounted by the Strm driver will save the Strm files locally +#### 保存Strm文件至本地 -::: - -::: zh-CN 开启后访问 strm 驱动中目录或被 strm 驱动挂载的目录时会将 strm 文件保存至本地 -::: - -#### SaveStrmLocalPath { lang="en" } - -#### 保存Strm文件本地路径 { lang="zh-CN" } +#### 保存Strm文件本地路径 -::: en -The local directory path where Strm files are stored. - -::: - -::: zh-CN strm文件保存的本地路径 -::: - -#### KeepLocalDownloadFile { lang="en" } - -#### 保留本地下载文件 { lang="zh-CN" } +#### 保留本地下载文件 -::: en -::: warning -Warning: Deprecated parameter, removed in version 4.1.9 and will be removed in future versions -::: -::: zh-CN ::: warning 警告:已废弃参数,4.1.9版本移除,后续版本将彻底移除 ::: -#### Local Save Mode { lang="en" } - -#### 本地保存模式 { lang="zh-CN" } - -::: en - -- `Insert Mode`: Only generate files that do not exist locally; existing local files will not be modified. -- `Update Mode`: Generate files that do not exist locally and update the content of existing local files to the latest - version. -- `Sync Mode`: Based on Update Mode, additionally delete local files that no longer exist on the cloud drive. - -> if you need scraper software to read local strm files and generate metadata files, please choose `Update Mode` to ensure that the content of local strm files is up to date and metadata files are not deleted - -::: - -::: zh-CN +#### 本地保存模式 - `新增模式`: 仅对本地没有的文件进行生成,对本地文件不进行任何操作 - `更新模式`: 对本地没有的文件进行生成同时更新本地文件内容至最新 @@ -235,23 +90,9 @@ Warning: Deprecated parameter, removed in version 4.1.9 and will be removed in f > 如果您需要刮削器等软件需要读取本地strm文件且生成元数据文件,请选择`更新模式`,以确保本地strm文件内容是最新的且不会删除元数据文件 -::: - -### Actively generate local files { lang="en" } - -### 主动生成本地文件 { lang="zh-CN" } - -::: en -The local file generation feature only takes effect when users access the corresponding directory. To recursively generate files for all paths, you can use the **Manually Scan** function, located under `Manage Page / Indices / Manually Scan`. In the **Path to scan** field, enter the **mount path of the Strm driver**, and in the **Rate limit** field, specify the API rate limit for the scanning process. Click start and wait for completion. - -Enabling the [Global Settings / Handle hook after writing](/configuration/global#handle-hook-after-writing) allows local files to be automatically generated after performing upload, rename, delete, move, copy, or extraction operations either under the original driver. -Scheduled or automatic local file generation by listening for driver changes is not currently supported. -::: - -::: zh-CN +### 主动生成本地文件 生成本地文件功能仅会在用户访问相应目录时生效,为了递归生成所有路径,可以使用**手动扫描**功能,其位于`管理界面 / 索引 / 手动扫描`下,**扫描路径**填写**Strm驱动的挂载路径**,**限制速率**填写扫描过程中的 API 限速,点击开始,等待完成即可。 开启[全局设置 / 写入操作后触发目录更新钩子](/configuration/global#写入操作后触发目录更新钩子),可以在原驱动下进行上传、重命名、删除、移动、复制、解压操作后自动生成本地文件。 暂不支持定时或监听驱动更改自动生成本地文件。 -::: diff --git a/pages/guide/drivers/teambition.md b/pages/guide/drivers/teambition.md index 1f550c21e..7772108fb 100644 --- a/pages/guide/drivers/teambition.md +++ b/pages/guide/drivers/teambition.md @@ -1,87 +1,27 @@ --- -title: - en: Teambition - zh-CN: Teambition -icon: iconfont icon-state -# This control sidebar order top: 681 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# Teambition + -::: en -Teambition project file, supports Chinese server and international server. -::: -::: zh-CN Teambition 工程文件,支持中国服务器和国际服务器。 -::: - -## Cookies { lang="en" } -## Cookies { lang="zh-CN" } +## Cookies -::: en -After login, the validity period is unknown -![cookie](/img/drivers/teambition-cookie.png) -::: -::: zh-CN 登录后获取,有效期未知 ![cookie](/img/drivers/teambition-cookie.png) -::: -## Project ID and root folder id { lang="en" } +## 项目 ID 和根文件夹 ID -## 项目 ID 和根文件夹 ID { lang="zh-CN" } - -::: en -Get it from the URL after logging in -![id](/img/drivers/teambition-id.png) -::: -::: zh-CN 登录后从网址获取 ![id](/img/drivers/teambition-id.png) -::: - -## The default download method used { lang="en" } -# 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +# 默认使用的下载方式 ```mermaid --- @@ -101,5 +41,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/teldrive.md b/pages/guide/drivers/teldrive.md index fc59b31c5..633553be2 100644 --- a/pages/guide/drivers/teldrive.md +++ b/pages/guide/drivers/teldrive.md @@ -1,39 +1,14 @@ --- -title: - en: Teldrive - zh-CN: Teldrive -icon: iconfont icon-state -# This control sidebar order top: 586 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - - -::: en -Teldrive is a Telegram-based cloud storage app maintained by a third-party open-source project: [tgdrive/teldrive](https://github.com/tgdrive/teldrive). +# Teldrive -**Highlights** - -- Unlimited storage -- No file size limit -- If you don’t subscribe to Telegram Premium, bandwidth is limited, and speed depends on the distance and bandwidth quality between your account’s data center (DC1–DC5) and the Teldrive server. + -Backend deployment requires a **Telegram API (not Bot API)**. See the official guide: [Teldrive installation](https://teldrive-docs.pages.dev/docs/getting-started/prerequisites) -::: -::: zh-CN Teldrive 是一个基于 Telegram 的云存储应用,由第三方开源仓库维护:[tgdrive/teldrive](https://github.com/tgdrive/teldrive)。 **功能特点** @@ -43,40 +18,15 @@ Teldrive 是一个基于 Telegram 的云存储应用,由第三方开源仓库 - 未订阅 Telegram Premium 时带宽会受限,速度取决于你的账户数据中心(DC1–DC5)与 Teldrive 服务之间的距离和带宽质量。 部署后端需要 **Telegram API(不是 Bot API)**。完整安装指南见:[Teldrive 安装教程](https://teldrive-docs.pages.dev/docs/getting-started/prerequisites) -::: - -## Address { lang="en" } -## 地址 { lang="zh-CN" } +## 地址 -::: en -Enter the base URL of your Teldrive backend **without** a trailing slash. - -Example: `https://teldrive.example.com` -::: -::: zh-CN 填写 Teldrive 后端的基础网址,**不要**包含末尾斜杠 `/`。 示例:`https://teldrive.example.com` -::: - -## Authentication (Cookie) { lang="en" } - -## 认证(Cookie) { lang="zh-CN" } - -::: en -Only **Cookie** authentication is supported. -After logging in to the Teldrive web UI, grab the cookie from your browser. +## 认证(Cookie) -The cookie should **start with** `access_token=` and is a JWT. - -![Fetch teldrive cookie](/img/drivers/teldrive/fetch_teldrive_cookie.png) - -::: tip -You only need the string **containing** `access_token=`. -::: -::: zh-CN 仅支持 **Cookie** 方式认证。 登录 Teldrive 网页端后,从浏览器中获取 Cookie。 @@ -89,84 +39,28 @@ Cookie 应当以 `access_token=` 开头,这是一个 JWT。 只需要**包含** `access_token=` 的那一串字符串。 ::: -## Download methods { lang="en" } +## 下载方式 -## 下载方式 { lang="zh-CN" } - -::: en -**Note**: If `Use Share Link` is enabled, a shared file link is created and the download URL is valid for **1 hour**. - -Otherwise, you need to enable OpenList's `Web Proxy`. -::: -::: zh-CN **注意**:如果启用了 `使用分享链接` 选项,将会创建共享文件链接,下载链接有效期为 **1 小时**。 否则,您需要启用 OpenList 的 Web 代理。 -::: - -## Chunk size { lang="en" } - -## 分块大小 { lang="zh-CN" } -::: en -Upload chunk size in **MiB**. +## 分块大小 -Default: `10` (10 MiB). If large uploads fail, try a smaller value. - -If the chunk size is larger than the file size, the file is uploaded in a single thread without chunking. -::: -::: zh-CN 上传时的分块大小,单位 **MiB**。 默认值:`10`(10 MiB)。若大文件上传失败,可尝试调小。 当分块大小大于文件本身大小时,不会分块,文件将以单线程上传。 -::: -## Upload Concurrency { lang="en" } +## 上传并发数量 -## 上传并发数量 { lang="zh-CN" } - -::: en -Concurrent upload threads. Default: `4`. - -Adjust based on available memory. A handy estimate is: -`memory ≈ chunk_size × concurrency` -::: -::: zh-CN 上传并发线程数,默认:`4`。 请根据可用内存调整,粗略计算: `内存占用 ≈ 分块大小 × 并发数` -::: -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -186,5 +80,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-policy" click c1 "../drivers/common.html#webdav-policy" ``` - -::: diff --git a/pages/guide/drivers/terabox.md b/pages/guide/drivers/terabox.md index e706cc58d..da04a3a6c 100644 --- a/pages/guide/drivers/terabox.md +++ b/pages/guide/drivers/terabox.md @@ -1,101 +1,38 @@ --- -title: - en: Terabox - zh-CN: Terabox(海外百度) -icon: iconfont icon-state -# This control sidebar order top: 594 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# Terabox(海外百度) + https://www.terabox.com/ -## Refresh Token { lang="en" } - -## 刷新令牌 { lang="zh-CN" } +## 刷新令牌 -::: en -~~[**Click here to log in**](https://www.terabox.com/) to obtain a refresh token~~ -Since F12 is currently disabled on the web page, we can first open F12 on another page and turn on the `Network` → `Preserve log` option. -f12
-Then enter https://www.terabox.com in the address bar of this tab to enter the webpage, you can see the reserved request, and then find **`Cookie`** from the reserved request. -![terabox](/img/drivers/terabox/terabox_cookie.png) -
-::: -::: zh-CN ~~[**点此登录**](https://www.terabox.com/)进行获取刷新令牌,随意找一个请求即可找到 **`Cookie`**~~ 由于目前网页端已经禁用了F12,我们可以先在别的页面打开F12将 `网络` → `保留日志` 选项打开 f12
然后在从这个标签页地址栏输入 https://www.terabox.com 进入网页,就能看到保留的请求,再从保留的请求找找到 **`Cookie`** ![terabox](/img/drivers/terabox/terabox_cookie.png)
-::: -## Download Api { lang="en" } +## 下载接口 -## 下载接口 { lang="zh-CN" } - -::: en -**Download api needs to choose `Crack`** ,before you can use -
-::: -::: zh-CN **下载接口需要选择 `非官方`** ,才可以使用
-::: - -## Root folder id { lang="en" } - -## 根文件夹ID { lang="zh-CN" } - -::: en -- Root folder id is `/` -- Mount other single subdirectories, put a picture for reference - - Demo - -::: - -::: zh-CN +## 根文件夹ID - 根目录为 `/` - 挂载其他单个子目录,放一张图参考一下吧 - Demo -::: - -## Download error using Proxy Url link { lang="en" } - -## 使用Proxy Url链接下载错误 { lang="zh-CN" } - -::: en -Using [**Proxy Url**](common.md#download-proxy-url) to download error - -```json -{ - "error_code": 31211, - "error_msg": "access denied" -} -``` +## 使用Proxy Url链接下载错误 -View specific reasons:**[https://github.com/alist-org/alist/issues/6508](https://github.com/alist-org/alist/issues/6508#issuecomment-2171762705)** -
-::: -::: zh-CN 使用了[**Proxy Url**](common.md#下载代理-url)进行下载报错 ```json @@ -107,35 +44,8 @@ View specific reasons:**[https://github.com/alist-org/alist/issues/6508](https 具体原因查看:**[https://github.com/alist-org/alist/issues/6508](https://github.com/alist-org/alist/issues/6508#issuecomment-2171762705)**
-::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -155,5 +65,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/thunder.md b/pages/guide/drivers/thunder.md index 970a3274d..cfc931003 100644 --- a/pages/guide/drivers/thunder.md +++ b/pages/guide/drivers/thunder.md @@ -1,41 +1,14 @@ --- -title: - en: Thunder Cloud Disk - zh-CN: 迅雷云盘 / X / 浏览器 -icon: iconfont icon-state -# This control sidebar order top: 685 -# A page can have multiple categories categories: - guide - drivers --- - - -::: en -::: tip -Please use `Thunder` directly instead of `ThunderExpert` if you are not good at it. - -`ThunderExpert` mainly provides more free settings and realizes more login methods - ---- - -`Thunder X` serves overseas users. As of the time of document release, only the Android version is available. Other versions have not yet been released. - -- Thunder X has sufficient speed even without membership. Future changes are unknown. -- Using the APP may require a proxy, but not when mounted on OpenList. - ---- - -`Thunder Browser`:Currently only supports mobile phones (Android, iOS) - -- **https://x.xunlei.com/** -- If you log in to OpenList, the mobile phone will be kicked offline. On the contrary, if you log in to OpenList first and then log in to the mobile phone, OpenList will be kicked offline but there will be no prompt +# 迅雷云盘 / X / 浏览器 -::: + -::: zh-CN ::: tip `迅雷` 前两个是服务**国内用户** @@ -59,72 +32,23 @@ Please use `Thunder` directly instead of `ThunderExpert` if you are not good at ::: -## 1. Thunder { lang="en" } - -## 1. 迅雷 { lang="zh-CN" } - -### Username { lang="en" } +## 1. 迅雷 -### 用户名 { lang="zh-CN" } +### 用户名 -::: en -That is, the mobile phone number, email, and username used for login (there is a probability that you cannot log in, you need to try) - -1. Before obtaining the verification code, do not include the `+86` country code when entering the phone number. -2. After obtaining the verification code, the phone number must include the `+86` country code, for example: +8613722223333. - -::: - -::: zh-CN 即用于登陆的手机号,邮箱,用户名(有概率无法登录,需要尝试) 1. 在获取验证码之前填写手机号先不要携带 `+86` 区号 2. 获取验证码后填写需要携带 `+86` 区号,例如 +8613722223333 这样填写 -::: - -### Password { lang="en" } +### 密码 -### 密码 { lang="zh-CN" } - -::: en -password for login -::: -::: zh-CN 即用于登陆的密码 -::: -
- -### Credit Key { lang="en" } - -### 信用密钥 { lang="zh-CN" } - -::: en -After saving for the first time, an error will occur. This is normal. Please go to the storage management interface. - -![Storage Error Interface](/img/drivers/xunlei/storage_error.png) - -Copy all the content above. - -[Click here to enter](https://i.xunlei.com/xlcaptcha/android.html) -After entering, open the developer tools (usually by pressing F12), go to the console, and enter `reviewCb()`. **Paste all the previously copied content, including the curly braces,** inside the parentheses, as shown below: - -![Console Input](/img/drivers/xunlei/input.png) - -Move the cursor to **after the parentheses** and press Enter. - -![SMS Verification](/img/drivers/xunlei/SMS.png) - -The webpage will automatically redirect to the mobile number verification page. - -After completing the verification, the page **will not** refresh automatically. Please check the console. The `creditkey` shown below is the required key. -![Console Output](/img/drivers/xunlei/output.png) +
-Copy it to the storage management, and you can mount it normally. +### 信用密钥 -::: -::: zh-CN 在第一次保存后会产生报错,这是正常的,请返回存储管理界面 ![存储报错界面](/img/drivers/xunlei/storage_error.png) @@ -149,34 +73,7 @@ Copy it to the storage management, and you can mount it normally. 将其复制到存储管理后即可正常挂载 -::: - -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -195,65 +92,20 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: - -## 2. 迅雷专家版 { lang="zh-CN" } +## 2. 迅雷专家版 -## 2. ThunderExpert { lang="en" } - -::: en -::: tip -If Xunlei needs to download, you must specify UserAgent (same as DownUserAgent below). Or use the proxy function in this program to transfer. -::: -::: zh-CN ::: tip 迅雷如果需要下载必须指定 UserAgent(同下 DownUserAgent),或使用本程序中的代理功能进行中转。 ::: -### 登录类型 { lang="zh-CN" } - -### Login Type { lang="en" } - -::: en - -1. When selecting User, you only need to fill in the username and password - - User You need to bring the `+86` area code, for example +8613722223333 fill in like this -2. **RefreshToken**: Just fill in RefreshToken when selecting RefreshToken - -::: - -::: zh-CN +### 登录类型 1. 选择 `用户名` 时填用户名和密码 - 用户名需要携带 `+86` 区号,例如 +8613722223333 这样填写 2. 选择 `刷新令牌` 时只需填写 `刷新令牌` -::: +### 签名类型 -### 签名类型 { lang="zh-CN" } - -### Signature Type { lang="en" } - -::: en -**Algorithms**:When selecting Algorithms, just fill in the Algorithms (it is difficult to obtain, and needs to be reversed) - -**Captcha sign**: Only fill in CaptchaSign and Timestamp when selecting CaptchaSign - -``` -//signature algorithm -str = ClientID + ClientVersion + PackageName + DeviceID + Timestamp -for (Algorithm in Algorithms) { - str = md5(str + Algorithm) -} -CaptchaSign = "1." + str -``` - -Login type and Sign type recommended options - -![xunlei](/img/drivers/xunlei/x3.png) - -::: -::: zh-CN 选择 `算法` 时需填写 `算法`(比较难获取,需要逆向) 选择 `验证码签名` 时只需填写 `验证码签名` 和 `时间戳` @@ -271,90 +123,25 @@ CaptchaSign = "1." + str ![xunlei](/img/drivers/xunlei/x3.png) -::: - -### 设备ID { lang="zh-CN" } - -### DeviceID { lang="en" } +### 设备ID -::: en -The value calculated by md5 is used to determine the logged in device -::: - -::: zh-CN 通过 MD5 计算的值,用于判断登录的设备 -::: -### 客户端ID, 客户端秘钥, 客户端版本, 包名 { lang="zh-CN" } +### 客户端ID, 客户端秘钥, 客户端版本, 包名 -### ClientID, ClientSecret, ClientVersion, PackageName { lang="en" } - -::: en -It is related to the signature, fill in according to the actual situation -::: -::: zh-CN 与签名有关,根据实际情况填写 -::: -### 用户代理 { lang="zh-CN" } +### 用户代理 -### UserAgent { lang="en" } - -::: en -UserAgent used by API requests, may be inaccessible or speed limited if the setting is incorrect -::: -::: zh-CN API 请求使用的 UserAgent,设置错误可能无法访问或限速 -::: -### 下载用户代理 { lang="zh-CN" } +### 下载用户代理 -### DownUserAgent { lang="en" } - -::: en -The User Agent used for downloading, if the setting is wrong, it will not be downloaded (it will be used when the agent is turned on) Fixed parameters: - -`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36` -::: -::: zh-CN 下载时用到的 User Agent,如果设置错误会无法下载(开启代理会使用) 固定参数: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36` -::: - -### 关键数据获取流程 { lang="zh-CN" } - -### Key data acquisition process { lang="en" } - -::: en -Obtain Xunlei request data through network analysis tools (packet capture tools) --- Please read the following information carefully - -Open Xunlei and log in to your account (the picture below uses the PC client to operate, and the web terminal is also available) - -Included in the request https://xluser-ssl.xunlei.com/v1/shield/captcha/init - -CaptchaSign, Timestamp, DeviceID, ClientID, ClientVersion, PackageName, User-Agent - -**Note**: After obtaining the information of the two pictures, slowly select the data from the two data captured and fill in it - -After logging in to Thunder and opening the packet capture tool, it is possible that the information of **v1/shield/captcha/init** cannot be immediately obtained at this time. - -Do not close the Xunlei PC client and the packet capture tool, just wait, it will refresh automatically after **5 minutes** - -You will see the parameters as shown in the figure below, and you can fill in it according to the acquisition (if you can't see it clearly, you can right-click to copy the picture and link it to the browser to open a new window) - -See **v1/shield/captcha/init** and grab it, please **immediately don't delay for a second** Right-click Xunlei in the taskbar in the lower right corner to exit, exit completely and then reopen to get \* \*Figure II\*\* -![xunlei](/img/drivers/xunlei/x4.png) - -Restarting Thunder will refresh the token - -The request to https://xluser-ssl.xunlei.com/v1/auth/token contains RefreshToken (please use the returned value), ClientSecret (does not exist on the web side) -![xunlei](/img/drivers/xunlei/x5.png) - -Figure 1 contains **7** parameters | Figure 1 contains **2** parameters | a fixed parameter (Down UserAgent), ten parameters and 3 options and a mount path, just write and save, before saving Remember to check~ -::: -::: zh-CN +### 关键数据获取流程 通过网络分析工具(抓包工具)获取迅雷请求数据 @@ -383,60 +170,18 @@ CaptchaSign、Timestamp、DeviceID、ClientID、ClientVersion、PackageName、Us ![xunlei](/img/drivers/xunlei/x5.png) 图一包含 **7 条** 参数 | 图一包含 \*\*2 条\*\* 参数 | 一条固定参数(Down UserAgent) ,十条参数 和 3 个选项 以及一个挂载路径,写好保存即可,保存前记得检查喔~~ -::: -### 使用视频URL { lang="zh-CN" } +### 使用视频URL -::: en -When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). - -The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. -::: - -::: zh-CN `视频URL` 开启后,会尝试使用接口返回的媒体链接进行替换原本的链接。目前官方对 `非会员` 情况下,下载链接进行了限速,但视频播放是不限速的。因此可以通过替换链接的方式来避免限速,这一点对于 `OpenList` 媒体文件下载和在线播放均生效。对于普通文件,也可以采取重命名文件后缀为媒体格式(例如:`.mp4` )的方式绕过限速。 专家版没默认开启 `视频URL`选项,目的是针对于自行配置详细参数的人群来说,应当知晓该选项的意义,因此默认不启用。对于 `迅雷X` 驱动默认启用该选项,更多的是为了小白用户考虑。 -::: - -### 迅雷专家版 完整的参数填演示图 { lang="zh-CN" } -### ThunderExpert complete parameter filling demo: { lang="en" } +### 迅雷专家版 完整的参数填演示图 -::: en ![xunlei](/img/drivers/xunlei/x6.png) -::: -::: zh-CN -![xunlei](/img/drivers/xunlei/x6.png) -::: - -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -457,61 +202,24 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -## 3. Thunder X { lang="en" } - -## 3. 迅雷 X { lang="zh-CN" } +## 3. 迅雷 X -::: zh-CN ::: danger 目前官方对于频繁调用接口行为会进行封号处理,请谨慎使用,后果自负。 ::: -::: en -::: danger -At present, the official will ban frequent API calls. Please use with caution and bear the consequences. -::: - -### 用户名、密码 { lang="zh-CN" } -### username、password { lang="en" } +### 用户名、密码 -::: en -The mailbox and password used for login -
-::: -::: zh-CN 即用于登陆的邮箱和密码
-::: -### 验证码 { lang="zh-CN" } +### 验证码 -### CaptchaToken { lang="en" } - -::: en -It will be filled automatically without filling in manually -
-::: -::: zh-CN 会自动填充,不用自己填写
-::: -### 根文件夹ID { lang="zh-CN" } +### 根文件夹ID -### Root folder id { lang="en" } - -::: en -The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it - -- In the request in the package`https://api-pan.xunleix.com/drive/v1/files?parent_id=&page_token=&filters=`,you can get the following parameters - - `文件夹ID(id)` - - `文件夹名称(name)` - - `父文件夹ID(parent_id)` -- The `folder ID` obtained in the root directory (for example : `我接收的文件`、`我的云盘`、`高速云下载`), **This will change with different account numbers, there is no the same value, In the request in the package** - ![xunlei](/img/drivers/xunlei/xlx_name.jpg) - -::: -::: zh-CN 默认为空展示全部目录,如果想用子文件夹做根目录请抓包获取 - 抓包请求中的`https://api-pan.xunleix.com/drive/v1/files?parent_id=&page_token=&filters=`,可以得到下面参数 @@ -521,51 +229,13 @@ The default is the full directory of the empty display, If you want to use a sub - 根目录下获取的`文件夹ID(Folder id)`(例如:`我接收的文件`、`我的云盘`、`高速云下载`),**这个会随着账号不同而变动,没有通用的值,自己抓包获取** ![xunlei](/img/drivers/xunlei/xlx_name.jpg) -::: - -### 使用视频URL { lang="zh-CN" } - -### Use video url { lang="en" } +### 使用视频URL -::: en -When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). - -The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. -::: - -::: zh-CN `视频URL` 开启后,会尝试使用接口返回的媒体链接进行替换原本的链接。目前官方对 `非会员` 情况下,下载链接进行了限速,但视频播放是不限速的。因此可以通过替换链接的方式来避免限速,这一点对于 `OpenList` 媒体文件下载和在线播放均生效。对于普通文件,也可以采取重命名文件后缀为媒体格式(例如:`.mp4` )的方式绕过限速。 专家版没默认开启 `视频URL`选项,目的是针对于自行配置详细参数的人群来说,应当知晓该选项的意义,因此默认不启用。对于 `迅雷X` 驱动默认启用该选项,更多的是为了小白用户考虑。 -::: - -### 默认使用的下载方式 { lang="zh-CN" } -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -586,63 +256,24 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: - -## 4. 迅雷 X 专家版 { lang="zh-CN" } - -## 4. Thunder X Expert { lang="en" } +## 4. 迅雷 X 专家版 -::: en -::: danger -At present, the official will ban frequent API calls. Please use with caution and bear the consequences. -::: -::: zh-CN ::: danger 目前官方对于频繁调用接口行为会进行封号处理,请谨慎使用,后果自负。 ::: -### 用户名、密码 { lang="zh-CN" } - -### username、password { lang="en" } +### 用户名、密码 -::: en -The mailbox and password used for login -
-::: -::: zh-CN 即用于登陆的邮箱和密码
-::: - -### 验证码 { lang="zh-CN" } -### CaptchaToken { lang="en" } +### 验证码 -::: en -It will be filled automatically without filling in manually -
-::: -::: zh-CN 会自动填充,不用自己填写
-::: -### 根文件夹ID { lang="zh-CN" } +### 根文件夹ID -### Root folder id { lang="en" } - -::: en -The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it - -- In the request in the package`https://api-pan.xunleix.com/drive/v1/files?parent_id=&page_token=&filters=`,you can get the following parameters - - `文件夹ID(id)` - - `文件夹名称(name)` - - `父文件夹ID(parent_id)` -- The `folder ID` obtained in the root directory (for example : `我接收的文件`、`我的云盘`、`高速云下载`), **This will change with different account numbers, there is no the same value, In the request in the package** - ![xunlei](/img/drivers/xunlei/xlx_name.jpg) - -::: -::: zh-CN 默认为空展示全部目录,如果想用子文件夹做根目录请抓包获取 - 抓包请求中的`https://api-pan.xunleix.com/drive/v1/files?parent_id=&page_token=&filters=`,可以得到下面参数 @@ -652,75 +283,17 @@ The default is the full directory of the empty display, If you want to use a sub - 根目录下获取的`文件夹ID(Folder id)`(例如:`我接收的文件`、`我的云盘`、`高速云下载`),**这个会随着账号不同而变动,没有固定一样的值,自己抓包获取** ![xunlei](/img/drivers/xunlei/xlx_name.jpg) -::: - -### 登录类型 { lang="zh-CN" } - -### Login Type { lang="en" } - -::: en - -- `User`: Only fill in the username and password -- `Refresh token`: Just fill in`RefreshToken` - -::: -::: zh-CN +### 登录类型 - `用户`:选择 `用户`时填`用户名和密码` - `刷新令牌`:选择 `刷新令牌` 时只需填写 `刷新令牌` -::: - -### 签名类型 { lang="zh-CN" } - -### Sign Type { lang="en" } - -::: en - -- `Algorithms`:Choose `Algorithms` Just fill in `Algorithms` (It has been automatically filled. You do n’t have to fill in it yourself. You only need to fill in the username and password.When the algorithm is not invalidated) -- `Captcha sign`: Choose `Captcha sign` Just fill in `Captcha sign` and `Timestamp` - -::: - -::: zh-CN +### 签名类型 - `算法`:选择 `算法(Algorithms)` 时需填写 `算法(Algorithms)` - `验证码签名`:选择 `验证码签名(Captcha sign)` 时只需填写 `验证码签名(Captcha sign)` 和 `时间戳(Timestamp)` -::: - -### 部分参数抓包说明 { lang="zh-CN" } - -### Part of parameter Packet Capture description { lang="en" } - -::: en - -- `Captcha token`:No need to fill in -- `Device id`:The value calculated by MD5 is used to judge the login device -- `Client id`, `Client secret`, `Client version`, `Package name`:It related to the signature, fill in according to the actual situation - ---- - -- `User agent`:API requests `User agent`, setting errors may not access or speed limit -- `Download user agent`:When downloading, use the `user agent `,If the setting error cannot be downloaded (the agent will use it) - - `User agent` and `Download user agent`:You can fill it out by yourself. If you don’t know how to fill it out, you can leave it blank and it will be automatically filled in. - ---- - -In the request in the package `https://xluser-ssl.xunleix.com/v1/shield/captcha/init`,you can get the following parameters6 - -- `Client id`、`Device id`、`Captcha sign` -- `Package name`、`Client version`、`Timestamp` - - ![xunlei](/img/drivers/xunlei/xlx_z1.jpg) - - In the request in the package `https://xluser-ssl.xunleix.com/v1/auth/signin`, you can get the following parameters2 - - `Client id`、`Client secret` - ![xunlei](/img/drivers/xunlei/xlx_z2.jpg) - -::: - -::: zh-CN +### 部分参数抓包说明 - `验证码`:无需填写 - `设备id`:通过 MD5 计算的值,用于判断登录的设备 @@ -746,51 +319,13 @@ In the request in the package `https://xluser-ssl.xunleix.com/v1/shield/captcha/ - `客户端ID(Client id)`、`客户端密钥(Client secret)` ![xunlei](/img/drivers/xunlei/xlx_z2.jpg) -::: +### 使用视频URL -### 使用视频URL { lang="zh-CN" } - -### Use video url { lang="en" } - -::: en -When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). - -The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. -::: - -::: zh-CN `视频URL` 开启后,会尝试使用接口返回的媒体链接进行替换原本的链接。目前官方对 `非会员` 情况下,下载链接进行了限速,但视频播放是不限速的。因此可以通过替换链接的方式来避免限速,这一点对于 `OpenList` 媒体文件下载和在线播放均生效。对于普通文件,也可以采取重命名文件后缀为媒体格式(例如:`.mp4` )的方式绕过限速。 专家版没默认开启 `视频URL`选项,目的是针对于自行配置详细参数的人群来说,应当知晓该选项的意义,因此默认不启用。对于 `迅雷X` 驱动默认启用该选项,更多的是为了小白用户考虑。 -::: - -### 默认使用的下载方式 { lang="zh-CN" } - -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -811,56 +346,20 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: - -## 5.迅雷浏览器 { lang="zh-CN" } - -## 5. Thunder Browser { lang="en" } +## 5.迅雷浏览器 -### 用户名、密码 { lang="zh-CN" } +### 用户名、密码 -### username、password { lang="en" } - -::: en -Mobile phone number, email, username, and password used to log in - -- When filling in the mobile phone number, please bring the `+86` area code, for example `+8613822334455` - -::: -::: zh-CN 即用于登陆的手机号,邮箱,用户名,以及密码 - 填写手机号要携带 `+86` 区号,例如 `+8613822334455` -::: - -### 验证码 { lang="zh-CN" } - -### CaptchaToken { lang="en" } +### 验证码 -::: en -It will be filled automatically without filling in manually -::: -::: zh-CN 会自动填充,不用自己填写 -::: - -### 根文件夹ID { lang="zh-CN" } -### Root folder id { lang="en" } +### 根文件夹ID -::: en -The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it - -- In the request in the package`https://x-api-pan.xunlei.com/drive/v1/files?parent_id&page_token&space=`,you can get the following parameters - - `文件夹ID(id)` - - `文件夹名称(name)` - - `父文件夹ID(parent_id)` -- The `folder ID` obtained in the root directory (for example : `来自分享`、`超级保险箱`), **This will change with different account numbers, there is no the same value, In the request in the package** - ![xunlei](/img/drivers/xunlei/x_br_foled.png) - -::: -::: zh-CN 默认为空展示全部目录,如果想用子文件夹做根目录请抓包获取 - 抓包请求中的`https://x-api-pan.xunlei.com/drive/v1/files?parent_id&page_token&space=`,可以得到下面参数 @@ -870,82 +369,24 @@ The default is the full directory of the empty display, If you want to use a sub - 根目录下获取的`文件夹ID(Folder id)`(例如:`来自分享`、`超级保险箱`),**这个会随着账号不同而变动,没有固定一样的值,自己抓包获取** ![xunlei](/img/drivers/xunlei/x_br_foled.png) -::: - -### 保险箱密码 { lang="zh-CN" } - -### Safe password { lang="en" } +### 保险箱密码 -::: en -Thunder Browser Safe password - -- Files in Safe password can only be deleted directly and cannot be deleted to the recycle bin, so the [**Deletion method**](#remove-way) below has nothing to do with this configuration. - -::: -::: zh-CN 迅雷浏览器云盘的保险箱密码 - 超级保险箱內文件只能直接删除 无法删除到回收站,所以下方[**删除方式**](#删除方式)与此配置无关 -::: - -### 删除方式 { lang="zh-CN" } - -### Remove way { lang="en" } +### 删除方式 -::: en -**Trash**:Use OpenList to delete files and then move them to the Recycle Bin. If you delete them accidentally, you can restore them through Thunder Cloud Disk. -**Delete**:Deleting it directly cannot restore it. -::: - -::: zh-CN **回收站**:在OpenList删除后移除到回收站,如果有误删可以通过迅雷云盘恢复 **删除**:直接删除不可以恢复找回 -::: - -### 使用视频URL { lang="zh-CN" } - -### Use video url { lang="en" } - -::: en -When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). -The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. -::: +### 使用视频URL -::: zh-CN `视频URL` 开启后,会尝试使用接口返回的媒体链接进行替换原本的链接。目前官方对 `非会员` 情况下,下载链接进行了限速,但视频播放是不限速的。因此可以通过替换链接的方式来避免限速,这一点对于 `OpenList` 媒体文件下载和在线播放均生效。对于普通文件,也可以采取重命名文件后缀为媒体格式(例如:`.mp4` )的方式绕过限速。 专家版没默认开启 `视频URL`选项,目的是针对于自行配置详细参数的人群来说,应当知晓该选项的意义,因此默认不启用。对于 `迅雷X` 驱动默认启用该选项,更多的是为了小白用户考虑。 -::: - -### 默认使用的下载方式 { lang="zh-CN" } -### The default download method used { lang="en" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -966,59 +407,21 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: - -## 6. 迅雷浏览器专家版 { lang="zh-CN" } - -## 6. Thunder Browser Expert { lang="en" } - -### 用户名、密码 { lang="zh-CN" } - -### username、password { lang="en" } +## 6. 迅雷浏览器专家版 -::: en -Mobile phone number, email, username, and password used to log in +### 用户名、密码 -- When filling in the mobile phone number, please bring the `+86` area code, for example `+8613822334455` - -::: -::: zh-CN 即用于登陆的手机号,邮箱,用户名,以及密码 - 填写手机号要携带 `+86` 区号,例如 `+8613822334455` -::: +### 验证码 -### 验证码 { lang="zh-CN" } - -### CaptchaToken { lang="en" } - -::: en -It will be filled automatically without filling in manually -
-::: -::: zh-CN 会自动填充,不用自己填写
-::: - -### 根文件夹ID { lang="zh-CN" } -### Root folder id { lang="en" } +### 根文件夹ID -::: en -The default is the full directory of the empty display, If you want to use a subfolder to make the root directory, grab the request to get it - -- In the request in the package`https://x-api-pan.xunlei.com/drive/v1/files?parent_id&page_token&space=`,you can get the following parameters - - `文件夹ID(id)` - - `文件夹名称(name)` - - `父文件夹ID(parent_id)` -- The `folder ID` obtained in the root directory (for example : `来自分享`、`超级保险箱`), **This will change with different account numbers, there is no the same value, In the request in the package** - ![xunlei](/img/drivers/xunlei/x_br_foled.png) - -::: - -::: zh-CN 默认为空展示全部目录,如果想用子文件夹做根目录请抓包获取 - 抓包请求中的`https://x-api-pan.xunlei.com/drive/v1/files?parent_id&page_token&space=`,可以得到下面参数 @@ -1028,104 +431,28 @@ The default is the full directory of the empty display, If you want to use a sub - 根目录下获取的`文件夹ID(Folder id)`(例如:`来自分享`、`超级保险箱`),**这个会随着账号不同而变动,没有固定一样的值,自己抓包获取** ![xunlei](/img/drivers/xunlei/x_br_foled.png) -::: - -### 保险箱密码 { lang="zh-CN" } - -### Safe password { lang="en" } - -::: en -Thunder Browser Safe password - -- Files in Safe password can only be deleted directly and cannot be deleted to the recycle bin, so the [**Deletion method**](#remove-way-1) below has nothing to do with this configuration. +### 保险箱密码 -::: -::: zh-CN 迅雷浏览器云盘的保险箱密码 - 超级保险箱內文件只能直接删除 无法删除到回收站,所以下方[**删除方式**](#删除方式-1)与此配置无关 -::: - -### 删除方式 { lang="zh-CN" } +### 删除方式 -### Remove way { lang="en" } - -::: en -**Trash**:Use OpenList to delete files and then move them to the Recycle Bin. If you delete them accidentally, you can restore them through Thunder Cloud -**Delete**:Deleting it directly cannot restore it. -::: -::: zh-CN **回收站**:在OpenList删除后移除到回收站,如果有误删可以通过迅雷云盘恢复 **删除**:直接删除不可以恢复找回 -::: -### 登录类型 { lang="zh-CN" } - -### Login Type { lang="en" } - -::: en - -- `User`: Only fill in the username and password -- `Refresh token`: Just fill in`RefreshToken` - -::: -::: zh-CN +### 登录类型 - `用户`:选择 `用户`时填`用户名和密码` - `刷新令牌`:选择 `刷新令牌` 时只需填写 `刷新令牌` -::: - -### 签名类型 { lang="zh-CN" } - -### Sign Type { lang="en" } - -::: en - -- `Algorithms`:Choose `Algorithms` Just fill in `Algorithms` (It has been automatically filled. You do n’t have to fill in it yourself. You only need to fill in the username and password.When the algorithm is not invalidated) -- `Captcha sign`: Choose `Captcha sign` Just fill in `Captcha sign` and `Timestamp` - -::: -::: zh-CN +### 签名类型 - `算法`:选择 `算法(Algorithms)` 时需填写 `算法(Algorithms)` - `验证码签名`:选择 `验证码签名(Captcha sign)` 时只需填写 `验证码签名(Captcha sign)` 和 `时间戳(Timestamp)` -::: - -### 部分参数抓包说明 { lang="zh-CN" } - -### Part of parameter Packet Capture description { lang="en" } - -::: en - -- `Captcha token`:No need to fill in -- `Device id`:The value calculated by MD5 is used to judge the login device -- `Client id`, `Client secret`, `Client version`, `Package name`:It related to the signature, fill in according to the actual situation - ---- - -- `User agent`:API requests `User agent`, setting errors may not access or speed limit -- `Download user agent`:When downloading, use the `user agent `,If the setting error cannot be downloaded (the agent will use it) - - `User agent` and `Download user agent`:You can fill it out by yourself. If you don’t know how to fill it out, you can leave it blank and it will be automatically filled in. - ---- - -In the request in the package `https://xluser-ssl.xunlei.com/v1/shield/captcha/init`,you can get the following parameters6 - -- `Client id`、`Device id`、`Captcha sign` -- `Package name`、`Client version`、`Timestamp` - ![xunlei](/img/drivers/xunlei/x_br_add1.png) - - In the request in the package `https://xluser-ssl.xunlei.com/v1/auth/signin/token`, you can get the following parameters3 - - `Client id`、`Client secret`、`Refresh token` - - ![xunlei](/img/drivers/xunlei/x_br_add2.png) - -::: - -::: zh-CN +### 部分参数抓包说明 - `验证码`:无需填写 - `设备id`:通过 MD5 计算的值,用于判断登录的设备 @@ -1150,29 +477,13 @@ In the request in the package `https://xluser-ssl.xunlei.com/v1/shield/captcha/i - `客户端ID(Client id)`、`客户端密钥(Client secret)`、`刷新令牌(Refresh token)` ![xunlei](/img/drivers/xunlei/x_br_add2.png) -::: - -### 使用视频URL { lang="zh-CN" } - -### Use video url { lang="en" } +### 使用视频URL -::: en -When the `Video URL` option is enabled, it will attempt to replace the original link with the media link returned by the API. Currently, the official platform throttles the download link for **non-members**, but video streaming is not throttled. Therefore, by replacing the link, you can avoid throttling, which applies to both **OpenList** media file downloads and streaming. For regular files, you can also bypass the throttling by renaming the file extension to a media format (e.g., `.mp4`). - -The **Expert Edition** does not enable the `Video URL` option by default, as it is intended for users who configure detailed parameters themselves, and they should be aware of the significance of this option. Hence, it is not enabled by default. For **Xunlei X** users, this option is enabled by default, primarily for the convenience of less experienced users. -::: - -::: zh-CN `视频URL` 开启后,会尝试使用接口返回的媒体链接进行替换原本的链接。目前官方对 `非会员` 情况下,下载链接进行了限速,但视频播放是不限速的。因此可以通过替换链接的方式来避免限速,这一点对于 `OpenList` 媒体文件下载和在线播放均生效。对于普通文件,也可以采取重命名文件后缀为媒体格式(例如:`.mp4` )的方式绕过限速。 专家版没默认开启 `视频URL`选项,目的是针对于自行配置详细参数的人群来说,应当知晓该选项的意义,因此默认不启用。对于 `迅雷X` 驱动默认启用该选项,更多的是为了小白用户考虑。 -::: - -### The default download method used { lang="en" } -### 默认使用的下载方式 { lang="zh-CN" } - -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -1192,28 +503,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: diff --git a/pages/guide/drivers/uc.md b/pages/guide/drivers/uc.md index 33d3d3eca..123306d58 100644 --- a/pages/guide/drivers/uc.md +++ b/pages/guide/drivers/uc.md @@ -1,91 +1,31 @@ --- -title: - en: UC / TV - zh-CN: UC / TV -icon: iconfont icon-state -# This control sidebar order top: 683 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - +# UC / TV -## UC Cloud { lang="en" } + -## UC 网盘 { lang="zh-CN" } +## UC 网盘 **https://drive.uc.cn/** -### Cookie { lang="en" } +### Cookie -### Cookie { lang="zh-CN" } - -::: en -Press F12 to open "Debug", select "Network", select any request on the left, and find the one with the `Cookie` parameter. -![](/img/drivers/uc/uc_cookie.png) -::: -::: zh-CN 在 `F12`任意请求中找到携带 `Cookie` 的值 ![](/img/drivers/uc/uc_cookie.png) -::: - -### Root Folder ID { lang="en" } - -### 根文件夹ID { lang="zh-CN" } - -::: en -Root Folder ID is `0` -- After entering the folder, get the directory ID in the top address bar. If the subdirectory is deeper, the directory ID will be at the back of the address bar. Just write the subdirectory ID you want to mount. - ![](/img/drivers/uc/uc_fileid.png) +### 根文件夹ID -::: -::: zh-CN 根目录ID为 `0` - 子文件夹ID进入文件夹后顶部地址栏获取目录ID,如果子目录越深目录ID在地址栏越靠后,想挂载哪个子目录就写那个子目录ID就可以 ![](/img/drivers/uc/uc_fileid.png) -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -104,34 +44,11 @@ flowchart TB click c1 "../drivers/common.html#webdav-策略" ``` -::: +## UC TV -## UC TV { lang="en" } - -## UC TV { lang="zh-CN" } - -::: en -The TV version supports `302`, but only `List` and `Download` operations are supported. Other operations are not supported (the interface does not support it). -::: -::: zh-CN TV 版支持 `302`,但是只能 `访问` 和 `下载` 两个操作,其它操作不支持(接口不支持) -::: - -### Add method { lang="en" } -### 添加方式 { lang="zh-CN" } - -::: en - -1. Select the `UC TV` driver, fill in the mounting path, and then save -2. Return to the all driver page and use the mobile APP to scan the QR code (If the QR code is not displayed, click on `Table Layout` in the upper right corner of the driver to switch from list mode to table mode) -3. After scanning the QR code to confirm, disable the driver, then enable the `driver` to use it. - - `Refresh token`、`Device id`、`Query token`,It will be filled in automatically, no manual filling is required - Please do not edit manually and modify it - ![](/img/drivers/tv_qrcode.png) - -::: - -::: zh-CN +### 添加方式 1. 选择 `UCTV` 驱动,填写挂载路径,然后保存 2. 返回全部驱动页面,使用手机APP扫描二维码(如果不显示二维码,驱动右上角点击点击`表格布局` 从列表模式切换成表格模式) @@ -139,54 +56,14 @@ TV 版支持 `302`,但是只能 `访问` 和 `下载` 两个操作,其它操 - `Refresh token 刷新令牌`、`Device id 设备ID `、`Query token` 会自动填充,不需要人工填写 - 请不要进行编辑手动修改 ![](/img/drivers/tv_qrcode.png) -::: - -### Root Folder ID { lang="en" } - -### 根文件夹ID { lang="zh-CN" } +### 根文件夹ID -::: en -Root Folder ID is `0` - -- After entering the folder, get the directory ID in the top address bar. If the subdirectory is deeper, the directory ID will be at the back of the address bar. Just write the subdirectory ID you want to mount. - ![](/img/drivers/uc/uc_fileid.png) - -::: -::: zh-CN 要挂载的根文件夹,默认为`0` - 子文件夹ID进入文件夹后顶部地址栏获取目录ID,如果子目录越深目录ID在地址栏越靠后 ![](/img/drivers/uc/uc_fileid.png) -::: - -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -206,5 +83,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/url_tree.md b/pages/guide/drivers/url_tree.md index 587103642..b471cb280 100644 --- a/pages/guide/drivers/url_tree.md +++ b/pages/guide/drivers/url_tree.md @@ -1,25 +1,12 @@ --- -title: - en: UrlTree - zh-CN: 地址树 -icon: iconfont icon-state -# This control sidebar order top: 998 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 地址树 + :::tip **`地址树-UrlTree`** 是什么? 是用来挂载单个文件链接的,详情可以查看 **→→→[原需求说明](https://github.com/alist-org/alist/issues/3268)←←←** ::: diff --git a/pages/guide/drivers/uss.md b/pages/guide/drivers/uss.md index 12c45e965..3e14a9d4d 100644 --- a/pages/guide/drivers/uss.md +++ b/pages/guide/drivers/uss.md @@ -1,38 +1,12 @@ --- -title: - en: UPYUN Storage Service - zh-CN: 又拍云存储 -icon: iconfont icon-state -# This control sidebar order top: 894 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' - - '官方' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -::: tip -When using the **302 redirect** strategy, the `_upd` and `_upt` parameters are automatically appended to the URL. If **"Parameter Follow"** is enabled in the Upyun console, the CDN nodes will treat these parameters as part of the file path, resulting in a **404 Not Found** error for existing files. - -Solutions: +# 又拍云存储 -- Method 1: In the Upyun console, under **"Cache Control"**, change **"Parameter Follow"** to **"Parameter Not Follow"**. - -- Method 2: Set the WebDAV policy of OpenList to **"Local Proxy"**. This mode avoids appending interfering parameters but will increase the server's data transfer. -::: - -::: zh-CN ::: tip 在使用 **302 重定向** 策略时,会自动在 URL 后拼接 `_upd` 与 `_upt` 参数,若又拍云控制台开启了 **“参数跟随”**,CDN 节点会将这些参数视为文件路径的一部分进行匹配,从而导致原本存在的文件返回 **404 Not Found**。 @@ -44,134 +18,45 @@ Solutions: ::: -::: en -UPYUN Storage Service,refer to as USS,[**Yupyun USS Official Website**](https://console.upyun.com/services/file/) -::: -::: zh-CN UPYUN 存储服务,简称 USS,[**又拍云USS官网**](https://console.upyun.com/services/file/) -::: -### BucKet { lang="en" } +## 存储桶 -## 存储桶 { lang="zh-CN" } - -::: en -UPYUN Storage Service Name -::: -::: zh-CN **UPYUN 存储桶服务名称** -::: -### Endpoint { lang="en" } +## Endpoint -## Endpoint { lang="zh-CN" } - -::: en -Accelerated domain name (default TEST domain or bound domain, not CNAME domain) -If you use http protocol, please add `http://` protocol header. -The test domain name provided by upyun is not accessible in some network and does not support https, so it is recommended to use your own bound domain name. -::: -::: zh-CN 加速域名(默认的测试域名或已绑定域名,不是CNAME域名) 如果使用http协议,请自行添加`http://`协议头 又拍云提供的默认测试域名在部分网络环境下无法访问,且不支持https,建议使用自行绑定的域名 -::: -### Operator name { lang="en" } +## 操作员名称 -## 操作员名称 { lang="zh-CN" } - -::: en -Operator name -::: -::: zh-CN 操作员名称 -::: - -### Operator password { lang="en" } -## 操作员密码 { lang="zh-CN" } +## 操作员密码 -::: en -Operator password -::: -::: zh-CN 操作员密码 -::: - -### Root folder path { lang="en" } -## 根文件夹 ID { lang="zh-CN" } +## 根文件夹 ID -::: en -Root path, if not filled, it defaults to the root directory. -::: -::: zh-CN 根路径,不填则默认为根目录。 -::: - -### Sign url expire { lang="en" } -## 签名链接有效期 { lang="zh-CN" } +## 签名链接有效期 -::: en -The validity period of the signed download address is 4 hours by default. -::: -::: zh-CN 签名下载地址的有效期默认为 4 小时。 -::: - -## Example of filling in details { lang="en" } -## 详情填写示意图 { lang="zh-CN" } +## 详情填写示意图 -::: en -::: tip -If you want to use the official test domain name, you must manually add http For example: http://xxx.test.upcdn.net -If you want to use HTTPS, of course, you can also add your own domain name, for example: https://you.xxx.com -permission is turned on by itself, and the read permission must be turned on! -::: -::: en -![s3](/img/drivers/s3/up.png) -::: - -::: zh-CN ::: tip 如果你要用官方提供的的测试域名那必须要手动加http 例如: http://xxx.test.upcdn.net 如果想用HTTPS,当然也可以添加自己的域名就可以使用例如:https://you.xxx.com 操作员的权限自己需要哪个开启哪个,读取权限必须得开! ::: -::: zh-CN -![s3](/img/drivers/s3/up.png) -::: -### The default download method used { lang="en" } - -### 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` +![s3](/img/drivers/s3/up.png) -::: -::: zh-CN +### 默认使用的下载方式 ```mermaid --- @@ -191,5 +76,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/webdav.md b/pages/guide/drivers/webdav.md index 75c08c730..cdb514c5a 100644 --- a/pages/guide/drivers/webdav.md +++ b/pages/guide/drivers/webdav.md @@ -1,95 +1,33 @@ --- -title: - en: WebDav - zh-CN: WebDAV -icon: iconfont icon-state -# This control sidebar order top: 899 -# A page can have multiple categories categories: - guide - drivers --- -## Address { lang="en" } +# WebDAV -## 地址 { lang="zh-CN" } +## 地址 -::: en -WebDAV root address -::: - -::: zh-CN WebDAV 根地址 -::: - -## Username { lang="en" } -## 用户名 { lang="zh-CN" } +## 用户名 -::: en -username -::: - -::: zh-CN 用户名 -::: - -## Password { lang="en" } -## 密码 { lang="zh-CN" } +## 密码 -::: en -password -::: - -::: zh-CN 密码 -::: - -## Root folder path { lang="en" } - -## 根文件夹ID { lang="zh-CN" } -::: en -The path of fodler you want to mount, same as join in address -::: +## 根文件夹ID -::: zh-CN 要挂载的文件夹路径,与加入地址相同 -::: -## Tls insecure skip verify { lang="en" } +## 跳过 SSL 证书验证 -## 跳过 SSL 证书验证 { lang="zh-CN" } - -::: en -Whether to skip SSL certificate verification. If your WebDAV server uses a self-signed certificate, you may need to enable this option. Enabling it will reduce security, please use it with caution. -::: - -::: zh-CN 是否跳过 SSL 证书验证。如果你的 WebDAV 服务器使用自签名证书,可能需要启用此选项。启用后会降低安全性,请谨慎使用。 -::: - -## Support 302 redirect { lang="en" } - -## 支持 302 重定向 { lang="zh-CN" } - -::: en - -In general, WebDAV servers return the file content directly. Due to the need for authorization, only proxy downloads are possible. However, some servers redirect to the actual file address, such as OpenList `WebDav Policy`'s `302 Redirect`. - -The `Web Proxy` option in WebDAV storage settings is enabled by default. If disabled, OpenList will attempt to obtain the redirected address and return it to the user device for direct download. -Prerequisites: - -1. The WebDAV server must support returning 302; if it returns 200, this feature cannot be used, and disabling Web Proxy will make it unusable. -2. The returned 302 address must be publicly accessible and must not require authorization information; otherwise, the user device cannot download it. -3. Disable the `Web Proxy` option in the storage settings. - -::: - -::: zh-CN +## 支持 302 重定向 通常情况下,WebDAV 服务器会直接返回文件内容,由于需要授权,只能代理下载。但一些服务器会重定向到实际的文件地址,如 OpenList `WebDav 策略` 的 `302 重定向`。 @@ -101,51 +39,15 @@ WebDAV 存储设置的 `Web 代理` 选项默认为开启状态,如果关闭 2. 返回的 302 地址必须是公开可访问的地址,不得需要授权信息,否则用户设备无法下载。 3. 在存储设置中关闭 `Web 代理` 选项。 -::: - -## OneDrive/SharePoint { lang="en" } - -## OneDrive/SharePoint { lang="zh-CN" } - -::: en -Select the vendor as sharepoint, and support the international version/21Vianet. - -For OneDrive/SharePoint, you can obtain the WebDAV root address through [this tool](https://tool.example.com/onedrive/webdav). If you want to mount the specified directory, you can join it later. +## OneDrive/SharePoint -The username is the OneDrive account email, and the password is the OneDrive account password. -::: - -::: zh-CN 选择 vendor 为 sharepoint,支持国际版/世纪互联。 你可以通过[这个工具](https://alist.example.com/tool/onedrive/webdav.html)获取 WebDAV 根地址,如果要挂载指定的目录,在后面拼接即可。 用户名为 OneDrive 账号邮箱,密码即为 OneDrive 账号密码。 -::: - -## Error message { lang="en" } - -## 错误提示 { lang="zh-CN" } - -::: en - -- **failed get objs: failed to list objs: PROPFIND/根目录:403** - Need to log in to [Entra ID](https://entra.microsoft.com/#view/Microsoft_AAD_IAM/TenantOverview.ReactView?Microsoft_AAD_IAM_legacyAADRedirect=true) - - Find 'Manage Security Default' and click to disable it(❗ Note: Closing this option will disable the Authenticator verification for the domain) - - ![webdav](/img/drivers/webdav/webdav_Security.png) - - Another situation is that the corresponding OneDrive account has not been used for a long time, which will also prompt this problem. Try to log in to the account again from the OneDrive web page. The system will prompt you to change the password. After updating the password, try again with the changed password. - -- **failed link: failed get link: redirect failed, status: 200** - - This error indicates that the WebDAV server does not support 302 redirects. You need to enable the `Web Proxy` option in the storage settings to use proxy downloads. - -::: - -::: zh-CN +## 错误提示 - **failed get objs: failed to list objs: PROPFIND/根目录:403** @@ -161,36 +63,7 @@ The username is the OneDrive account email, and the password is the OneDrive acc 此错误表示 WebDAV 服务器不支持 302 重定向,需要在存储设置中启用 `Web 代理` 选项以使用代理下载。 -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[local proxy]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[302]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: - -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -210,5 +83,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/weiyun.md b/pages/guide/drivers/weiyun.md index a500b46c5..ce6209d7a 100644 --- a/pages/guide/drivers/weiyun.md +++ b/pages/guide/drivers/weiyun.md @@ -1,43 +1,16 @@ --- -title: - en: Tencent weiyun - zh-CN: 腾讯微云 -icon: iconfont icon-state -# This control sidebar order top: 682 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '本地代理' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- - - -::: en -Tencent Weiyun official website: **https://www.weiyun.com** -::: -::: en -::: tip Weiyun login validity period reminder - -- QQ: The login can only keep alive for 48 hours at present, and the cookie has to be replaced -- WeChat: no such problem, use token - It is recommended to use WeChat to log in +# 腾讯微云 -::: + -::: zh-CN 腾讯微云官网:**https://www.weiyun.com** -::: -::: zh-CN + ::: tip 微云登录有效期提示 - QQ:登录目前只能保活48小时就得更换cookie @@ -46,42 +19,15 @@ Tencent Weiyun official website: **https://www.weiyun.com** ::: -## Root folder file_id { lang="en" } - -## 根文件夹ID { lang="zh-CN" } - -::: en - -1. Display the root directory, **`leave blank`**, you don’t need to fill in, the program will automatically fill in -2. If only a single folder ID is displayed, select the folder to be displayed, Copy the ID behind the top address bar - - ![](/img/drivers/weiyun/weiyun_fl_id.png) - -::: - -::: zh-CN +## 根文件夹ID 1. 展示根目录,**`留空`** 就可以不用填写,程序会自动填充 2. 如果只展示其中单个文件夹ID,进入想展示的文件夹內然后复制顶部地址栏后面的ID ![](/img/drivers/weiyun/weiyun_fl_id.png) -::: - -## Cookie { lang="en" } - -## Cookie { lang="zh-CN" } - -::: en -After logging in to Weiyun, **Open the developer debugging tool (F12)**, find the request that carries the cookie in any request, copy and fill in it. +## Cookie -- The token obtained by WeChat login is longer than the cookie field of QQ login -- QQ WeChat fills in the `Cookie` field value, and you can see it by just looking for a request - - ![](/img/drivers/weiyun/weiyun_cookie.png) - -::: -::: zh-CN 登录微云后,**打开开发者调试工具(F12)**,在任意请求中找到携带cookie的请求复制填写就可以。 - 微信登录获取的token比QQ登录的cookie字段要长 @@ -89,51 +35,12 @@ After logging in to Weiyun, **Open the developer debugging tool (F12)**, find th ![](/img/drivers/weiyun/weiyun_cookie.png) -::: - -## Other notes { lang="en" } - -## 其他说明 { lang="zh-CN" } - -::: en - -1. The copy function cannot be used -2. **It is recommended not to share Weiyun with the outside world. After all, it is bound to your own QQ number. If you have a major loss to QQ or other things after being blocked/frozen, you will be responsible for the consequences** - -::: - -::: zh-CN +## 其他说明 1. 复制功能无法使用 2. **建议不要将微云对外共享,毕竟是绑定的自己QQ号码,如果被封/冻结之后对QQ或者其它产生了重大损失后果自负** -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style c1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - c1 - end - c1[local proxy]:::someclass==default===>a2[user equipment] - classDef someclass fill:#f96 - b1[Download proxy URL]-.alternative.->a2[user equipment] - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -151,5 +58,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/wopan.md b/pages/guide/drivers/wopan.md index b17debdf8..8acdc555d 100644 --- a/pages/guide/drivers/wopan.md +++ b/pages/guide/drivers/wopan.md @@ -1,44 +1,18 @@ --- -title: - en: WoPan - zh-CN: 中国联通云盘 -icon: iconfont icon-state -# This control sidebar order top: 693 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- +# 中国联通云盘 + [https://pan.wo.cn/](https://pan.wo.cn/) -## GetToken { lang="en" } - -## 获取令牌 { lang="zh-CN" } - -::: en -::: tip -The difference between the two token acquisition methods: - -- Method 1: **Token valid for seven days** Logging in to the web version of China Unicom Cloud Drive will disconnect and invalidate the mount point on OpenList's side. However, logging in to the mobile version is fine, and thereby you can have both open simultaneously. - -- Method 2: **Token valid for two months** There isn't any problem to log into the Unicom Cloud Drive. However, it will be kicked off if you log in on the mobile side. - -::: +## 获取令牌 -::: zh-CN ::: tip 两个令牌获取方法的区别: @@ -48,21 +22,7 @@ The difference between the two token acquisition methods: ::: -### Method 1 { lang="en" } - -### 方法一 { lang="zh-CN" } - -::: en - -1. Open developer tools -2. Open the official website to log in -3. Find the request with this content: - ![WoPan token request screenshot](/img/drivers/wopan/wopan-req.png) -4. Find the token in the response: - ![WoPan token response screenshot](/img/drivers/wopan/wopan-resp.png) - -::: -::: zh-CN +### 方法一 1. 打开开发者工具 2. 打开官网 登录 @@ -71,22 +31,7 @@ The difference between the two token acquisition methods: 4. 在响应中找到token: ![WoPan token response screenshot](/img/drivers/wopan/wopan-resp.png) -::: - -### Method 2 { lang="en" } - -### 方法二 { lang="zh-CN" } - -::: en - -1. Open developer tools -2. Open the official website to login -3. Find the Apps tab in Developer Tools and select Session Storage: - ![WoPan session storage screenshot](/img/drivers/wopan/wopan-session-storage.png) -4. Follow the arrows in the diagram to find the `Refresh Token` and the `Access Token` - -::: -::: zh-CN +### 方法二 1. 打开开发者工具 2. 打开官网 登录 @@ -94,83 +39,24 @@ The difference between the two token acquisition methods: ![WoPan session storage screenshot](/img/drivers/wopan/wopan-session-storage.png) 4. 根据图中箭头指引找到`刷新令牌`和`访问令牌` -::: - -## Root folder ID { lang="en" } - -## 根文件夹ID { lang="zh-CN" } - -::: en - -- **Personal cloud:**:**0** - - Single folder ID:Unknown (wait for replenishment) -- **Family cloud**:Unknown (wait for replenishment) - - Family cloud Single folder ID:Unknown (wait for replenishment) - -::: - -::: zh-CN +## 根文件夹ID - 个人云:**0** - 单独文件夹ID:未知(后续补充) - 家庭云:未知(后续补充) - 家庭云单独文件夹ID:未知(后续补充) -::: - -## Type { lang="en" } +## 类型 -## 类型 { lang="zh-CN" } - -::: en -Personal cloud:Put the `family ID` blank is the personal cloud -Family cloud:add `Family ID` Log in to the [SmartHome] -> [我的] -> [您的家], click"+" [Invite] -> [微信邀请], send the link to yourself, open it and copy all the text after 'groupId='. -::: -::: zh-CN 个人云:将`Family ID`空着就是个人云 家庭云:填写`Family ID` 登录【联通智家APP】->【我的】->【您的家】,点击"+"【邀请家人】->【微信邀请】,将链接发送给自己,打开后复制“groupId=”后面的全部文本 -::: - -## OpenList fill in examples: { lang="en" } -## OpenList挂载填写示例: { lang="zh-CN" } +## OpenList挂载填写示例: -::: en -Data obtained by using tools `Refresh_token Fill in the refresh token`, `Access_token fills in access_token` -![WoPan add example screenshot](/img/drivers/wopan/add-wopan.png) -::: -::: zh-CN 将使用工具获取的 `refresh_token填入刷新令牌`,`access_token填入访问令牌` ![WoPan add example screenshot](/img/drivers/wopan/add-wopan.png) -::: - -## The default download method used { lang="en" } -## 默认使用的下载方式 { lang="zh-CN" } - -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -190,5 +76,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/drivers/wps.md b/pages/guide/drivers/wps.md index 69053bf9e..247f63761 100644 --- a/pages/guide/drivers/wps.md +++ b/pages/guide/drivers/wps.md @@ -1,28 +1,14 @@ --- -title: - en: WPS - zh-CN: WPS 云文档 -# This control sidebar order top: 675 -# A page can have multiple categories categories: - guide - drivers --- - - -::: en -OpenList supports mounting WPS cloud document (which is also called KDocs) web. - -Official websites: - -- KDocs (Personal): -- WPS 365 (Business/Enterprise/Education): +# WPS 云文档 -::: + -::: zh-CN OpenList 支持挂载 WPS 云文档(金山文档)的网页端。 官方网站: @@ -30,38 +16,14 @@ OpenList 支持挂载 WPS 云文档(金山文档)的网页端。 - 金山文档(个人版): - WPS 365(商业/企业/教育版): -::: - ## Cookie -::: en -::: warning -Please make sure to obtain the Cookie using a new browser environment or incognito mode to avoid including session information from other accounts. - -A session can only be logged in to one account at a time. -::: - -::: zh-CN ::: warning 请务必使用一个新的浏览器环境或者无痕模式获取 Cookie,避免 Cookie 中包含其他账号的会话信息。 一个会话只能同时登录一个账号。 ::: -::: en -The cookie of WPS cloud storage can be obtained by following the steps below: - -1. Create a new browser environment or open an incognito window. -2. Visit the corresponding WPS cloud document website and log in to your WPS account. -3. Open the developer tools (press F12 or right-click and select "Inspect"). -4. Go to the "Network" tab and refresh the page. -5. Search for `islogin` in the network requests and open one of them. -6. In the request details, find the "Headers" section and look for the `Cookie` and `User-Agent` fields. -7. Copy the entire value of relevant fields and use them in OpenList. - -::: - -::: zh-CN WPS 云盘的 Cookie 可以通过以下步骤获取: 1. 创建一个新的浏览器环境或者打开无痕模式窗口。 @@ -72,39 +34,15 @@ WPS 云盘的 Cookie 可以通过以下步骤获取: 6. 在请求详情中找到“Headers”部分,查找`Cookie`、`User-Agent`字段。 7. 复制相关字段的完整值,并在 OpenList 中使用它。 -::: - -## Mode { lang="en" } - -## 模式 { lang="zh-CN" } - -::: en -There are two modes of API: - -- Personal -- Business - -Please select the appropriate mode based on your account type when adding the WPS cloud storage in OpenList. +## 模式 -::: - -::: zh-CN WPS 云文档有两种 API 模式: - 个人版 - 商业版 请根据你的账号类型在 OpenList 中添加 WPS 云文档时选择相应的模式。 -::: - -## Custom UA { lang="en" } -## 自定义 UA { lang="zh-CN" } +## 自定义 UA -::: en -You can set a custom User-Agent for requests to WPS cloud document. -::: - -::: zh-CN 可以给 WPS 云文档的请求设置自定义用户代理。 -::: diff --git a/pages/guide/drivers/yandex.md b/pages/guide/drivers/yandex.md index 3772e9b70..0ba0df659 100644 --- a/pages/guide/drivers/yandex.md +++ b/pages/guide/drivers/yandex.md @@ -1,74 +1,21 @@ --- -title: - en: Yandex Disk - zh-CN: Yandex云盘 -icon: iconfont icon-state -# This control sidebar order top: 589 -# A page can have multiple categories categories: - guide - drivers -# A page can have multiple tags -tag: - - Storage - - Guide - - '302' -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -## Refresh token { lang="en" } +# Yandex云盘 -## 刷新令牌 { lang="zh-CN" } +## 刷新令牌 -::: en -[Click here](https://oauth.yandex.com/authorize?response_type=code&client_id=a78d5a69054042fa936f6c77f9a0ae8b) to get the refresh token. -::: -::: zh-CN [点击这里](https://oauth.yandex.com/authorize?response_type=code&client_id=a78d5a69054042fa936f6c77f9a0ae8b) 来获取刷新令牌。 -::: -## Root folder path { lang="en" } +## 根文件夹ID -## 根文件夹ID { lang="zh-CN" } - -::: en -The root foler to mount, defaults to `/` -::: -::: zh-CN 要挂载的根文件夹,默认为 `/` -::: - -## The default download method used { lang="en" } - -## 默认使用的下载方式 { lang="zh-CN" } -::: en - -```mermaid ---- -title: Which download method is used by default? ---- -flowchart TB - style a1 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff - style a2 fill:#ff7575,stroke:#333,stroke-width:4px - subgraph ide1 [ ] - a1 - end - a1[302]:::someclass====|default|a2[user equipment] - classDef someclass fill:#f96 - c1[local proxy]-.alternative.->a2[user equipment] - b1[Download proxy URL]-.alternative.->a2[user equipment] - click a1 "../drivers/common.html#webdav-policy" - click b1 "../drivers/common.html#webdav-policy" - click c1 "../drivers/common.html#webdav-policy" -``` - -::: -::: zh-CN +## 默认使用的下载方式 ```mermaid --- @@ -88,5 +35,3 @@ flowchart TB click b1 "../drivers/common.html#webdav-策略" click c1 "../drivers/common.html#webdav-策略" ``` - -::: diff --git a/pages/guide/index.md b/pages/guide/index.md index 9c226f7e9..009002620 100644 --- a/pages/guide/index.md +++ b/pages/guide/index.md @@ -1,59 +1,22 @@ --- -title: - en: Quick Start - zh-CN: 快速开始 categories: - guide top: 100000 --- -::: en -OpenList is a file list program that supports multiple storage🗂️, a resilient, long-term governance, community-driven fork of AList🔀 — built to defend open source against trust-based attacks🛡️. +# 快速开始 -You can follow the steps below to quickly get started with OpenList 🚀: -::: - -::: zh-CN OpenList 是一个支持多种存储的文件列表程序🗂️,是一个有韧性、长期治理、社区驱动的 AList 分支🔀,旨在防御基于信任的开源攻击🛡️。 您可以参照以下步骤快速开始使用 OpenList 🚀: -::: - -::: en -::: info Still using Alist V3? -Here's your complete migration guide from Alist V3 to OpenList: - -[🚀 Migration Guide](/guide/migrate) -::: -::: zh-CN ::: info 仍在使用 Alist V3? 这里为您提供从 Alist V3 迁移到 OpenList 的完整指南: [🚀 迁移指南](/guide/migrate) ::: -## Installation 🖥️ { lang="en" } - -## 安装 🖥️ { lang="zh-CN" } - -::: en - -- [One-click Script 🎯](/guide/installation/script) -- [Manual installation ⚙️](/guide/installation/manual) -- [Use Docker 🐋](/guide/installation/docker) -- [Use 1Panel 📟](/guide/installation/1panel) -- [Use TrueNAS Scale 🗄️](/guide/installation/truenas) -- [Use Koyeb ☁️](/guide/installation/koyeb) -- [Desktop 💻](/guide/installation/desktop) -- [PaaS ☁️](/guide/installation/paas) -- [Run from source 🏗️](/guide/installation/source) -- [Download ⬇️](/guide/installation/download) -- [Reverse proxy 🔄](/guide/installation/reverse-proxy) - -::: - -::: zh-CN +## 安装 🖥️ - [一键脚本 🎯](/guide/installation/script) - [手动安装 ⚙️](/guide/installation/manual) @@ -67,41 +30,11 @@ Here's your complete migration guide from Alist V3 to OpenList: - [下载 ⬇️](/guide/installation/download) - [反向代理 🔄](/guide/installation/reverse-proxy) -::: - -## Storage Setup 💾 { lang="en" } - -## 添加存储 💾 { lang="zh-CN" } - -::: en - -- [Common 🗂️](/guide/drivers/common) - -::: - -::: zh-CN +## 添加存储 💾 - [通用项 🗂️](/guide/drivers/common) -::: - -## Configuration ⚙️ { lang="en" } - -## 配置 ⚙️ { lang="zh-CN" } - -::: en - -- [Configuration 📝](/configuration/configuration) -- [Site Configuration 🌐](/configuration/site) -- [Style Configuration 🎨](/configuration/style) -- [Preview Configuration 👀](/configuration/preview) -- [Global Configuration 🌍](/configuration/global) -- [Other Configuration 🔧](/configuration/other) -- [Side Configuration 📐](/configuration/side) - -::: - -::: zh-CN +## 配置 ⚙️ - [配置文件 📝](/configuration/configuration) - [网站设置 🌐](/configuration/site) @@ -111,40 +44,13 @@ Here's your complete migration guide from Alist V3 to OpenList: - [其他设置 🔧](/configuration/other) - [侧边栏设置 📐](/configuration/side) -::: - ## FAQ ❓ -::: en - -- [OpenList Error-Code 🚨](/faq/error) -- [How to 🛠️](/faq/howto) -- [Why 🤔](/faq/why) -- [See also 🔍](/faq/seealso) - -::: - -::: zh-CN - - [OpenList 错误码 🚨](/faq/error) - [怎么做 🛠️](/faq/howto) - [为什么 🤔](/faq/why) - [See also 🔍](/faq/seealso) -::: - -## Ecosystem 🏞️ { lang="en" } - -## 生态系统 🏞️ { lang="zh-CN" } - -::: en - -- [See more 🔍](/ecosystem) +## 生态系统 🏞️ -::: - -::: zh-CN - -- [查看更多 🔍](/ecosystem) - -::: +- [查看更多 🔍](/ecosystem/) diff --git a/pages/guide/installation/1panel.md b/pages/guide/installation/1panel.md index 3889fd845..d3ed9a653 100644 --- a/pages/guide/installation/1panel.md +++ b/pages/guide/installation/1panel.md @@ -1,39 +1,16 @@ --- -title: - en: Use 1Panel - zh-CN: 使用 1Panel -icon: iconfont icon-geometry -# This control sidebar order top: 45 -# A page can have multiple categories categories: - guide - installation --- -## Install { lang="en" } +# 使用 1Panel -## 安装 { lang="zh-CN" } +## 安装 -### Install 1Panel { lang="en" } +### 安装 1Panel -### 安装 1Panel { lang="zh-CN" } - -::: en -First, install 1Panel on your server. - -Run the following **one-click installation script** as the **root user** to automatically download and install 1Panel: - -```bash -bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)" -``` - -> 📖 **Detailed Installation Guide**: Please refer to the [1Panel Official Installation Documentation](https://1panel.cn/docs/v2/installation/online_installation/) - -After installation, log in to 1Panel using the provided **access address** and **initial account credentials**. -::: - -::: zh-CN 首先需要在服务器上安装 1Panel。 以 **root 用户身份**运行以下**一键安装脚本**,自动完成 1Panel 的下载和安装: @@ -45,35 +22,9 @@ bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_star > 📖 **详细安装说明**:请参考 [1Panel 官方安装文档](https://1panel.cn/docs/v2/installation/online_installation/) 安装完成后,通过提示的**访问地址**和**初始账号密码**登录 1Panel。 -::: - -### Install OpenList { lang="en" } - -### 安装 OpenList { lang="zh-CN" } -::: en -Log in to 1Panel, go to the **App Store**, search for **openlist**, and click **Install**. +### 安装 OpenList -![search](/img/1panel/search.png) - -> During installation, please configure the following parameters according to actual needs: -> -> - **Version**: Select the latest stable version -> - **WebUI Port**: Default is `5244`, can be modified as needed -> - **S3 Port**: Default is `5246`, can be modified as needed -> - **Pre-installed Environment**: -> - `Thumbnail`: Pre-install ffmpeg -> - `Offline Download`: Pre-install aria2 -> - `All of the above`: Pre-install ffmpeg & aria2 -> - **Timezone**: Recommended to set to `Asia/Shanghai` -> - **Advanced Settings**: Be sure to check **External Port Access** - -> Keeping the **default configuration** can also complete the installation, but it is recommended to adjust according to actual needs. - -![install](/img/1panel/install.png) -::: - -::: zh-CN 登录 1Panel,进入 **应用商店**,搜索 **openlist**,点击**安装**即可。 ![search](/img/1panel/search.png) @@ -93,24 +44,9 @@ Log in to 1Panel, go to the **App Store**, search for **openlist**, and click ** > 保持**默认配置**也可以完成安装,但建议根据实际需求调整。 ![install](/img/1panel/install.png) -::: - -### Use OpenList { lang="en" } - -### 使用 OpenList { lang="zh-CN" } -::: en -After installation, go to the **Installed** page and click **Open** to access the OpenList **WebUI**. - -![installed](/img/1panel/installed.png) +### 使用 OpenList -> It is recommended to set the **Default Access Address** in the panel settings before using the application. - -> If you later configure a **reverse proxy**, update the **Web Access Address** in `Installed → Parameters`. - -::: - -::: zh-CN 安装完成后,进入 **已安装** 页面,点击 **跳转** 即可进入 OpenList 的 **WebUI** 页面。 ![installed](/img/1panel/installed.png) @@ -119,35 +55,8 @@ After installation, go to the **Installed** page and click **Open** to access th > 如果后续配置了**反向代理**,可以在 `已安装 → 参数` 页面修改 **Web 访问地址**。 -::: - -### Get Default Account & Password { lang="en" } - -### 获取默认账户密码 { lang="zh-CN" } - -::: en -Go to the **Container List**, find the **OpenList container**, and click **Terminal**. Then run the following commands inside the container: - -- **Generate a random password**: - - ```bash - ./openlist admin random - ``` - -- **Manually set a password**: - - ```bash - ./openlist admin set NEW_PASSWORD - ``` - -> **Note**: Replace `NEW_PASSWORD` with your desired password. - -![container](/img/1panel/container.png) - -![terminal](/img/1panel/terminal.png) -::: +### 获取默认账户密码 -::: zh-CN 进入 **容器列表**,找到 **OpenList 容器**,点击 **终端** 按钮进入容器内执行以下命令: - **生成随机密码**: @@ -167,4 +76,3 @@ Go to the **Container List**, find the **OpenList container**, and click **Termi ![container](/img/1panel/container.png) ![terminal](/img/1panel/terminal.png) -::: diff --git a/pages/guide/installation/desktop.md b/pages/guide/installation/desktop.md index 37c67e65c..7adb279b9 100644 --- a/pages/guide/installation/desktop.md +++ b/pages/guide/installation/desktop.md @@ -1,36 +1,15 @@ --- -title: - en: Desktop - zh-CN: 桌面版 -icon: iconfont icon-computer top: 40 categories: - guide - installation -tag: - - Install - - Guide -sticky: true -star: true comment: false --- -## What is OpenList Desktop { lang="en" } +# 桌面版 -## OpenList Desktop 是什么 { lang="zh-CN" } +## OpenList Desktop 是什么 -::: en -OpenList Desktop is a cross-platform desktop application that provides a user-friendly interface for managing OpenList and performing local mounts via Rclone. Built with Vue 3, Tauri, and Rust, it serves as a comprehensive solution for: - -- Managing OpenList core -- Mounting and managing cloud storage (WebDAV) -- Monitoring service status and uptime -- Providing system tray integration for background operations - -**Repository**: [OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) -::: - -::: zh-CN OpenList Desktop 是一个跨平台桌面应用程序,为管理 OpenList和通过 Rclone 进行本地挂载提供用户友好的界面。基于 Vue 3、Tauri 和 Rust 构建,是一个全面的解决方案,用于: - 管理 OpenList 核心 @@ -39,27 +18,10 @@ OpenList Desktop 是一个跨平台桌面应用程序,为管理 OpenList和通 - 提供系统托盘集成以进行后台操作 **仓库地址**: [OpenListTeam/OpenList-Desktop](https://github.com/OpenListTeam/OpenList-Desktop) -::: - -## Features { lang="en" } -## 功能特性 { lang="zh-CN" } - -### Core Features { lang="en" } - -### 核心功能 { lang="zh-CN" } - -::: en - -- **OpenList Service Management**: Start, stop, and monitor OpenList core -- **Local Mounting**: Mount via Rclone to the local file system -- **Real-time Monitoring**: Track service status, uptime, and performance metrics -- **Log Management**: View and manage OpenList and Rclone logs -- **System Tray**: Background operation with system tray notifications - -::: +## 功能特性 -::: zh-CN +### 核心功能 - **OpenList 服务管理**:启动、停止和监控 OpenList 核心 - **本地挂载**:通过 Rclone 挂载至本地文件系统 @@ -67,23 +29,7 @@ OpenList Desktop 是一个跨平台桌面应用程序,为管理 OpenList和通 - **日志管理**:查看和管理 OpenList 和 Rclone 日志 - **系统托盘**:带系统托盘通知的后台操作 -::: - -### Management Features { lang="en" } - -### 管理功能 { lang="zh-CN" } - -::: en - -- **Service Control**: Start/stop/restart OpenList and Rclone services -- **Configuration Management**: GUI-based configuration -- **Log Monitoring**: Real-time log viewing and management -- **Update Management**: Automatic update checking and installation -- **Auto-startup**: Configure applications to start with system boot - -::: - -::: zh-CN +### 管理功能 - **服务控制**:启动/停止/重启 OpenList 和 Rclone 服务 - **配置管理**:基于 GUI 的服务配置 @@ -91,29 +37,12 @@ OpenList Desktop 是一个跨平台桌面应用程序,为管理 OpenList和通 - **更新管理**:自动更新检查和安装 - **自动启动**:配置应用程序与系统一起启动 -::: - -## Screenshots { lang="en" } - -## 应用截图 { lang="zh-CN" } +## 应用截图 -### Home Dashboard { lang="en" } - -### 主页仪表板 { lang="zh-CN" } +### 主页仪表板 ![dashboard](/img/desktop/dashboard.png) -::: en -The main dashboard provides a comprehensive overview of your OpenList Desktop environment with: - -- OpenList backend status monitoring -- Quick action buttons for common tasks -- OpenList and Rclone version management -- Service management controls - -::: - -::: zh-CN 主仪表板提供您的 OpenList Desktop 环境的全面概览: - OpenList 后台状态监控 @@ -121,25 +50,10 @@ The main dashboard provides a comprehensive overview of your OpenList Desktop en - OpenList 和 Rclone 版本管理 - 服务管理控制 -::: - -### Mount Management { lang="en" } - -### 挂载管理 { lang="zh-CN" } +### 挂载管理 ![mount](/img/desktop/mount.png) -::: en -Easily perform local mounts: - -- Add and configure storage remotes -- Mount/unmount cloud storage -- Monitor mount status and statistics -- Configure auto-mounting options - -::: - -::: zh-CN 轻松进行本地挂载: - 添加和配置存储远程 @@ -147,27 +61,10 @@ Easily perform local mounts: - 监控挂载状态和统计信息 - 配置自动挂载选项 -::: - -### Log Management { lang="en" } - -### 日志管理 { lang="zh-CN" } +### 日志管理 ![logs](/img/desktop/logs.png) -::: en -Manage logs of multiple sources: - -- OpenList logs -- Rclone logs -- Application logs -- Filter and search logs -- Real-time log updates -- Export logs to file and clipboard - -::: - -::: zh-CN 管理多个来源的日志: - OpenList 日志 @@ -177,134 +74,47 @@ Manage logs of multiple sources: - 实时日志更新 - 导出日志到文件和剪贴板 -::: - -## Installation { lang="en" } - -## 安装 { lang="zh-CN" } - -### System Requirements { lang="en" } +## 安装 -### 系统要求 { lang="zh-CN" } - -::: en - -- **Operating System**: Windows 10+, macOS 10.15+, or Linux (Ubuntu 18.04+) - -::: - -::: zh-CN +### 系统要求 - **操作系统**:Windows 10+、macOS 10.15+ 或 Linux(Ubuntu 18.04+) -::: - -### Download Options { lang="en" } - -### 下载选项 { lang="zh-CN" } - -#### GitHub Releases (Recommended) { lang="en" } +### 下载选项 -#### GitHub 发行版(推荐) { lang="zh-CN" } +#### GitHub 发行版(推荐) -::: en -Download the latest release from [GitHub Releases](https://github.com/OpenListTeam/OpenList-Desktop/releases): - -- **Windows**: `OpenList-Desktop_x.x.x_{arch}-setup.exe` -- **macOS**: `OpenList-Desktop_x.x.x_{arch}.dmg` -- **Linux**: `OpenList-Desktop_x.x.x_{arch}.deb` or `OpenList-Desktop_x.x.x_{arch}.rpm` - -::: - -::: zh-CN 从 [GitHub Releases](https://github.com/OpenListTeam/OpenList-Desktop/releases) 下载最新版本: - **Windows**:`OpenList-Desktop_x.x.x_{arch}-setup.exe` - **macOS**:`OpenList-Desktop_x.x.x_{arch}.dmg` - **Linux**:`OpenList-Desktop_x.x.x_{arch}.deb` 或 `OpenList-Desktop_x.x.x_{arch}.rpm` -::: - -#### Using Package Managers { lang="en" } +#### 使用包管理器 -#### 使用包管理器 { lang="zh-CN" } - -##### Windows - Winget { lang="en" } - -##### Windows - Winget { lang="zh-CN" } +##### Windows - Winget ```bash winget install OpenListTeam.OpenListDesktop ``` -### Installation Steps { lang="en" } - -### 安装步骤 { lang="zh-CN" } +### 安装步骤 -#### Windows { lang="en" } - -#### Windows { lang="zh-CN" } - -::: en - -1. Download the `.exe` installer -2. Run the installer as Administrator -3. Follow the installation wizard -4. Launch from Start Menu or Desktop shortcut - -::: - -::: zh-CN +#### Windows 1. 下载 `.exe` 安装程序 2. 以管理员身份运行安装程序 3. 按照安装向导进行操作 4. 从开始菜单或桌面快捷方式启动 -::: - -#### macOS { lang="en" } - -#### macOS { lang="zh-CN" } - -::: en - -1. Download the `.dmg` file -2. Open the DMG and drag OpenList Desktop to Applications -3. Right-click and select "Open" (first time only) -4. Grant necessary permissions when prompted - -::: - -::: zh-CN +#### macOS 1. 下载 `.dmg` 文件 2. 打开 DMG 并将 OpenList Desktop 拖到应用程序文件夹 3. 右键单击并选择"打开"(仅首次) 4. 在提示时授予必要权限 -::: - -#### Linux { lang="en" } - -#### Linux { lang="zh-CN" } - -::: en - -1. Download the `.deb` or `.rpm` package -2. Use your package manager to install: - -```bash -# For Debian/Ubuntu -sudo dpkg -i OpenList-Desktop_x.x.x_amd64.deb - -# For CentOS/RHEL/Fedora -sudo rpm -i OpenList-Desktop_x.x.x_amd64.rpm -``` - -::: - -::: zh-CN +#### Linux 1. 下载 `.deb` 或 `.rpm` 包 2. 使用包管理器安装: @@ -317,28 +127,10 @@ sudo dpkg -i OpenList-Desktop_x.x.x_amd64.deb sudo rpm -i OpenList-Desktop_x.x.x_amd64.rpm ``` -::: - -## Getting Started { lang="en" } - -## 开始使用 { lang="zh-CN" } - -### First Launch { lang="en" } - -### 首次启动 { lang="zh-CN" } +## 开始使用 -::: en -::: tip -It is recommended to run OpenList Desktop with Administrator privileges on first launch to ensure proper service installation and configuration. -::: - -1. **Initial Setup**: On first launch, the application will guide you through initial configuration -2. **Service Installation**: Install the OpenList service when prompted -3. **Storage Configuration**: Configure your first cloud storage connection +### 首次启动 -::: - -::: zh-CN ::: tip 建议在首次启动时通过管理员权限运行 OpenList Desktop,以确保正确安装和配置服务。 ::: @@ -347,51 +139,15 @@ It is recommended to run OpenList Desktop with Administrator privileges on first 2. **服务安装**:在提示时安装 OpenList 服务 3. **存储配置**:配置您的第一个云存储连接 -::: +### 基本操作 -### Basic Operations { lang="en" } - -### 基本操作 { lang="zh-CN" } - -#### Starting Services { lang="en" } - -#### 启动服务 { lang="zh-CN" } - -::: en - -1. Navigate to **Dashboard** tab -2. Click **Start OpenList Core** in Quick Actions -3. Click **Start Rclone Backend** if needed - -::: - -::: zh-CN +#### 启动服务 1. 导航到 **仪表板** 选项卡 2. 在快速操作中点击 **启动 OpenList 核心** 3. 如需要,点击 **启动 Rclone 后端** -::: - -#### Adding Cloud Storage { lang="en" } - -#### 添加云存储 { lang="zh-CN" } - -::: en - -1. Navigate to **Mount** tab -2. Click **Add Remote** button -3. Configure storage settings: - - **Name**: Unique identifier for the storage - - **Type**: Storage provider (WebDAV) - - **URL**: Storage endpoint URL - - **Credentials**: Username and password - - **Mount Point**: Local directory path -4. Click **Save** and **Mount** - -::: - -::: zh-CN +#### 添加云存储 1. 导航到 **挂载** 选项卡 2. 点击 **添加远程** 按钮 @@ -403,93 +159,32 @@ It is recommended to run OpenList Desktop with Administrator privileges on first - **挂载点**:本地目录路径 4. 点击 **保存** 和 **挂载** -::: - -#### Monitoring Operations { lang="en" } - -#### 监控操作 { lang="zh-CN" } - -::: en - -- **Service Status**: Check service health indicators on the dashboard -- **Logs**: Use the Logs tab to monitor system operations -- **Performance**: View uptime and response metrics on the dashboard - -::: - -::: zh-CN +#### 监控操作 - **服务状态**:检查仪表板上的服务健康指示器 - **日志**:使用日志选项卡监控系统操作 - **性能**:在仪表板上查看运行时间和响应指标 -::: - -### Advanced Features { lang="en" } - -### 高级功能 { lang="zh-CN" } - -#### Auto-Mount Configuration { lang="en" } - -#### 自动挂载配置 { lang="zh-CN" } +### 高级功能 -::: en -Configure storages to automatically mount at startup with custom Rclone flags for optimal performance: +#### 自动挂载配置 -- `--vfs-cache-mode=full`: Enable full VFS caching -- `--buffer-size=256M`: Increase buffer size -- `--transfers=10`: Concurrent transfer limit - -::: - -::: zh-CN 配置存储在启动时自动挂载,使用自定义 Rclone 标志以获得最佳性能: - `--vfs-cache-mode=full`:启用完整 VFS 缓存 - `--buffer-size=256M`:增加缓冲区大小 - `--transfers=10`:并发传输限制 -::: - -#### System Tray Operations { lang="en" } - -#### 系统托盘操作 { lang="zh-CN" } - -::: en - -- **Right-click tray icon** for quick actions -- **Double-click** to show/hide main window -- **Background operation** with notifications - -::: - -::: zh-CN +#### 系统托盘操作 - **右键单击托盘图标** 进行快速操作 - **双击** 显示/隐藏主窗口 - **后台操作** 带通知功能 -::: - -## Configuration { lang="en" } - -## 配置 { lang="zh-CN" } - -### Application Settings { lang="en" } - -### 应用程序设置 { lang="zh-CN" } - -::: en -Access comprehensive settings management including: +## 配置 -- **OpenList Core Configuration**: Port, data directory, auto-launch settings -- **Startup Preferences**: Auto-startup and automation options -- **Theme and Language**: UI customization options -- **Update Settings**: Automatic update preferences +### 应用程序设置 -::: - -::: zh-CN 访问全面的设置管理,包括: - **OpenList 核心配置**:端口、数据目录、自动启动设置 @@ -497,63 +192,17 @@ Access comprehensive settings management including: - **主题和语言**:UI 自定义选项 - **更新设置**:自动更新偏好设置 -::: - -## Troubleshooting { lang="en" } - -## 故障排除 { lang="zh-CN" } - -### Common Issues { lang="en" } - -### 常见问题 { lang="zh-CN" } - -::: en - -- **Service won't start**: Check if ports are available and run as Administrator -- **Mount fails**: Verify storage credentials and network connectivity -- **Performance issues**: Adjust Rclone cache settings and buffer sizes -- **Update problems**: Check internet connection and proxy settings - -::: +## 故障排除 -::: zh-CN +### 常见问题 - **服务无法启动**:检查端口是否可用并以管理员身份运行 - **挂载失败**:验证存储凭据和网络连接 - **性能问题**:调整 Rclone 缓存设置和缓冲区大小 - **更新问题**:检查网络连接和代理设置 -::: - -## Build from Source { lang="en" } - -## 从源码构建 { lang="zh-CN" } - -::: en -For developers who want to build from source: - -```bash -# Clone the repository -git clone https://github.com/OpenListTeam/OpenList-Desktop.git -cd openlist-desktop - -# Install dependencies -yarn install - -# Prepare development environment -yarn run prebuild:dev - -# Development mode -yarn tauri dev +## 从源码构建 -# Build the application -yarn run build -yarn run tauri build -``` - -::: - -::: zh-CN 对于想要从源码构建的开发者: ```bash @@ -574,5 +223,3 @@ yarn tauri dev yarn run build yarn run tauri build ``` - -::: diff --git a/pages/guide/installation/docker.md b/pages/guide/installation/docker.md index 8cafb7f0a..f5c7ea52e 100644 --- a/pages/guide/installation/docker.md +++ b/pages/guide/installation/docker.md @@ -1,17 +1,12 @@ --- -title: - en: Use Docker - zh-CN: 使用 Docker -icon: iconfont icon-geometry -# This control sidebar order top: 50 -# A page can have multiple categories categories: - guide - installation --- -::: zh-CN +# 使用 Docker + ::: warning - 在 `v4.1.0` 以后的版本中(不包含 `v4.1.0`),OpenList 镜像已经移除了 `PUID`、`PGID`,并借鉴于 MariaDB 的构建方式,使用 `useradd` 增加了用户 `openlist`(UID 1001)和组 `openlist`(GID 1001),并使用该用户运行 `openlist server`。 @@ -23,79 +18,37 @@ categories: - **rootless** 模式中的 Docker, `--user 0:0` 代表当前用户的 UID 和 GID。请确保您在运行容器时,正确设置了 `--user` 参数,以确保文件权限的正确性。 ::: -::: en -::: warning - -- In version `v4.1.0` and later (excluding `v4.1.0`), OpenList has removed the `PUID` and `PGID` environment variables in the image, and has adopted a method similar to that of MariaDB, where a user named `openlist` (UID 1001) and a group named `openlist` (GID 1001) are created, and `openlist server` runs under this user. - - This means you need to manually handle the permission issues of the mapped directory, ensuring that the OpenList user (1001) inside the container has access to the mapped directory. - - You can also run the container with the `--user UID:GID` option to specify the user and group under which OpenList runs inside the container, allowing it to access the mapped directory. - -- In the **rootless** mode of Docker, `--user 0:0` represents the current user's UID and GID. Please ensure that you set the `--user` parameter correctly when running the container to ensure proper file permissions. - ::: - -## Install { lang="en" } - -## 安装 { lang="zh-CN" } +## 安装 ### Docker CLI -::: en -Install Docker. And run the command below: -::: - -::: zh-CN 安装好 Docker 后,执行以下命令: -::: -#### v4.1.0 以后版本 { lang="zh-CN" } +#### v4.1.0 以后版本 -#### For version after v4.1.0 { lang="en" } - -::: zh-CN ::: warning 请注意:`/etc/openlist` 仅为默认映射的目录,您可以根据需要修改为其他目录。 ::: -::: en -::: warning -Please note: `/etc/openlist` is just the default mapped directory, you can change it to another directory as needed. -::: - -::: zh-CN ::: tip 如果您希望使用当前用户运行和管理 OpenList 及其配置目录,请使用以下命令: ::: -::: en -::: tip -If you are using the current user to run and manage OpenList and its configuration directory, please use the following command: -::: - ```bash mkdir -p /etc/openlist docker run --user $(id -u):$(id -g) -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest ``` -::: zh-CN ::: tip 如果您希望使用 1001,即容器内置的默认 `openlist` 用户运行和管理 OpenList 及其配置目录,请使用以下命令: ::: -::: en -::: tip -If you want to run and manage OpenList and its configuration directory using the default OpenList user (1001) inside the container, please use the following command: -::: - ```bash sudo chown -R 1001:1001 /etc/openlist docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest ``` -#### v4.1.0 及以前版本 { lang="zh-CN" } - -#### For version v4.1.0 and earlier { lang="en" } +#### v4.1.0 及以前版本 ```bash docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e PUID=0 -e PGID=0 -e UMASK=022 --name="openlist" openlistteam/openlist:latest @@ -103,12 +56,7 @@ docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 52 ### Docker Compose -::: en -Create `docker-compose.yml` file. -::: -::: zh-CN 创建 `docker-compose.yml` 文件。 -::: ```bash mkdir -p /opt/openlist @@ -116,16 +64,9 @@ cd /opt/openlist vim docker-compose.yml ``` -::: en -Write the content below. Then save and exit. -::: -::: zh-CN 写入以下内容,然后保存并退出: -::: -#### For version after v4.1.0 { lang="en" } - -#### v4.1.0 以后版本 { lang="zh-CN" } +#### v4.1.0 以后版本 ```yaml # docker-compose.yml @@ -143,9 +84,7 @@ services: restart: unless-stopped ``` -#### For version v4.1.0 and earlier { lang="en" } - -#### v4.1.0 及以前版本 { lang="zh-CN" } +#### v4.1.0 及以前版本 ```yaml # docker-compose.yml @@ -164,99 +103,31 @@ services: restart: unless-stopped ``` -::: en -Run commands in the same path of `docker-compose.yml` file: -::: -::: zh-CN 在 `docker-compose.yml` 相同目录下执行: -::: ```bash docker compose pull docker compose up -d ``` -## Env { lang="en" } - -## 环境变量 { lang="zh-CN" } - -::: en -| Name | Default | Desc | -|:------------|:--------|----------------------------------------------------------------------------------------------------------------------------| -| `PUID` | `0` | User UID, Deprecated in v4.1.0 later versions| -| `PGID` | `0` | User GID, Deprecated in v4.1.0 later versions| -| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | -| `TZ` | `UTC` | Default is the UTC time zone. If you want to specify a time zone, you can set this variable, for example: `Asia/Shanghai`. | -| `RUN_ARIA2` | | Whether to run ARIA2 concurrently, default is `true` if aria2 is pre-installed, otherwise it is `false`. | -| `OPENLIST_ADMIN_PASSWORD` | | Set the password of admin by environment variable | -::: -::: zh-CN -| 名称 | 默认值 | 说明 | -| :---------- | :----- | -------------------------------------------------------------------------- | -| `PUID` | `0` | 运行身份 UID,在 v4.1.0 以后的版本中废弃 | -| `PGID` | `0` | 运行身份 GID,在 v4.1.0 以后的版本中废弃 | -| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | -| `TZ` | `UTC` | 默认为 UTC 时区,如果你想指定时区,则可以设置此变量,例如:`Asia/Shanghai` | -| `RUN_ARIA2` | | 是否同时运行 ARIA2,当镜像含有 aria2 环境时默认为 `true`,否则为 `false` | -| `OPENLIST_ADMIN_PASSWORD` | | 通过环境变量指定管理员密码 | -::: +## 环境变量 -::: en -Additionally, OpenList supports passing [configuration](/configuration/configuration) through environment variables. The OpenList in the Docker image runs by default with the `--no-prefix` flag, so you don't need to add the `OPENLIST_` prefix. +| 名称 | 默认值 | 说明 | +| :------------------------ | :----- | -------------------------------------------------------------------------- | +| `PUID` | `0` | 运行身份 UID,在 v4.1.0 以后的版本中废弃 | +| `PGID` | `0` | 运行身份 GID,在 v4.1.0 以后的版本中废弃 | +| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | +| `TZ` | `UTC` | 默认为 UTC 时区,如果你想指定时区,则可以设置此变量,例如:`Asia/Shanghai` | +| `RUN_ARIA2` | | 是否同时运行 ARIA2,当镜像含有 aria2 环境时默认为 `true`,否则为 `false` | +| `OPENLIST_ADMIN_PASSWORD` | | 通过环境变量指定管理员密码 | -You can view all available environment variables online in Go Packages. -::: -::: zh-CN 此外,OpenList 支持将 [配置](/configuration/configuration) 通过环境变量的方式传入。Docker 镜像中的 OpenList 默认使用 `--no-prefix` 参数运行,因此您无需添加 `OPENLIST_` 前缀。 您可以在 Go Packages 中在线查看所有可以使用的环境变量。 -::: https://pkg.go.dev/github.com/OpenListTeam/OpenList/v4/internal/conf#Config -## Image Versions { lang="en" } - -## 镜像版本 { lang="zh-CN" } - -::: en - -- Stable version: `openlistteam/openlist:latest` or `openlistteam/openlist:v*.*.*` - - Latest image version tag, please refer to https://hub.docker.com/r/openlistteam/openlist/tags - - - Some PaaS platforms do not support images larger than 100MB. Please use the lightweight image `lite`, for example: `openlistteam/openlist:latest-lite`. Otherwise, you may encounter the following error: `Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.` - -- Dev version: `openlistteam/openlist:beta` - -Pre-installed environment image suffix: - -| Suffix | Desc | -| :------- | ----------------------------------------------------------------------- | -| `aio` | An image that includes all of the following pre-installed environments. | -| `ffmpeg` | Pre-installed FFmpeg image for generating thumbnail for local storage | -| `aria2` | Pre-installed aria2 image for offline downloading. | - -You can append a suffix using the `-` symbol after any of the mirror tags to switch to an image with the corresponding environment. For example, `openlistteam/openlist:latest-aio` `openlistteam/openlist:latest-aria2` `openlistteam/openlist:latest-ffmpeg`. - ---- - -If the thumbnail generation function still does not work when using the pre-installed ffmpeg, please confirm: - -- You are using local storage -- Switched to grid view -- The thumbnail switch in local storage driver settings is enabled -- The configuration path for the thumbnail cache folder in local storage is correct, for example, `data/thumbnail` - ---- - -When using a pre-installed aria2 mirror, you might see errors like the following in the OpenList logs: - -``` -ERRO[2022-11-20 12:05:19] error [unaligned 64-bit atomic operation] while run task [download http://xxx.com/xxx.png to [/ftp](/)] -``` - -The solution is, if the CPU architecture is 64-bit, you can try to manually pull a 64-bit image or rebuild the container. If the CPU architecture is 32-bit, there is currently no available solution. -::: -::: zh-CN +## 镜像版本 - 稳定版:`openlistteam/openlist:latest` or `openlistteam/openlist:v*.*.*` - 最新镜像版本 Tag,请参阅:https://hub.docker.com/r/openlistteam/openlist/tags @@ -293,48 +164,23 @@ ERRO[2022-11-20 12:05:19] error [unaligned 64-bit atomic operation] while run ta ``` 解决方法是,如果是 CPU 架构是 64 位,可以尝试手动拉取 64 位镜像或重新构建容器。 如果是 CPU 架构是 32 位,目前尚无可用方案。 -::: -## See the admin's info{ lang="en" } +## 查看管理员信息 -## 查看管理员信息 { lang="zh-CN" } - -### First run { lang="en" } - -### 首次运行 { lang="zh-CN" } +### 首次运行 ```bash docker logs openlist ``` -::: en -You will see the admin password in the log. -::: -::: zh-CN 你将在日志中看到密码。 -::: ``` Successfully created the admin user and the initial password is: xYZabHGf ``` -### Not first run { lang="en" } +### 非首次运行 -### 非首次运行 { lang="zh-CN" } - -::: en -You can **randomly generate** or **manually set** - -```bash -# Randomly generate password -docker exec -it openlist ./openlist admin random - -# Manually set password to `NEW_PASSWORD` (replace this) -docker exec -it openlist ./openlist admin set NEW_PASSWORD -``` - -::: -::: zh-CN 您可以**重新生成**或**手动设置密码**。 ```bash @@ -345,54 +191,20 @@ docker exec -it openlist ./openlist admin random docker exec -it openlist ./openlist admin set NEW_PASSWORD ``` -::: - -## Update { lang="en" } - -## 更新 { lang="zh-CN" } +## 更新 ### Watchtower -::: en -If you find it troublesome, you can complete the update with a single line using Watchtower. -::: -::: zh-CN 如果嫌麻烦,可以使用 Watchtower 一键更新。 -::: ```bash docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower openlist --cleanup --run-once ``` -::: en -If the container name is not named `openlist`, please replace it to the actual one. -::: -::: zh-CN 如果您的容器名不是 `openlist`,请替换为实际的名称。 -::: ### Docker CLI -::: en - -```bash -# View the container (find the ID of the OpenList container) -docker ps -a - -# Stop running OpenList container instance, otherwise it cannot be deleted (this time the ID of the OpenList container is d429749a6e69, it is different for each installation) -docker stop ID - -# Delete the OpenList container (the data is still there as long as you don't delete it manually) -docker rm ID - -# Pull the latest image of OpenList -docker pull openlistteam/openlist:latest -``` - -::: - -::: zh-CN - ```bash # 查看容器(查找 OpenList 容器的 ID) docker ps -a @@ -407,17 +219,8 @@ docker rm ID docker pull openlistteam/openlist:latest ``` -::: - -#### 升级到 v4.1.0 以后版本 (不包括v4.1.0) { lang="zh-CN" } +#### 升级到 v4.1.0 以后版本 (不包括v4.1.0) -#### Upgrade to version v4.1.0 and later (excluding v4.1.0) { lang="en" } - -::: en -::: tip -If you want to run and manage OpenList and its configuration directory using the default OpenList user (1001) inside the container, please use the following command: -::: -::: zh-CN ::: tip 如果您希望使用 1001,即容器内置的默认 `openlist` 用户运行和管理 OpenList 及其配置目录,请使用以下命令: ::: @@ -427,11 +230,6 @@ chown -R 1001:1001 /etc/openlist docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest ``` -::: en -::: tip -If you are using the current user to run and manage OpenList and its configuration directory, please use the following command: -::: -::: zh-CN ::: tip 如果您希望使用当前用户运行和管理 OpenList 及其配置目录,请使用以下命令: ::: @@ -441,9 +239,7 @@ sudo chown -R $(id -u):$(id -g) /etc/openlist docker run --user $(id -u):$(id -g) -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e UMASK=022 --name="openlist" openlistteam/openlist:latest ``` -#### 升级到 v4.1.0 以前的版本(包括 v4.1.0) { lang="zh-CN" } - -#### Upgrade to version v4.1.0 and earlier (including v4.1.0) { lang="en" } +#### 升级到 v4.1.0 以前的版本(包括 v4.1.0) ```bash docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 5244:5244 -e PUID=0 -e PGID=0 -e UMASK=022 --name="openlist" openlistteam/openlist:latest @@ -451,12 +247,7 @@ docker run -d --restart=unless-stopped -v /etc/openlist:/opt/openlist/data -p 52 ### Docker Compose -::: en -Enter the same path of `docker-compose.yml` file and run: -::: -::: zh-CN 进入 `docker-compose.yml` 相同目录,然后运行: -::: ```bash docker compose pull @@ -464,32 +255,18 @@ docker compose down docker compose up -d ``` -## Advanced Docker Compose { lang="en" } - -## 增强版 Docker Compose { lang="zh-CN" } +## 增强版 Docker Compose -::: en -Create `docker-compose.yml` file. -::: -::: zh-CN 创建 `docker-compose.yml` 文件。 -::: ```bash mkdir -p /opt/openlist vim docker-compose.yml ``` -::: en -Write the content below. Then save and exit. -::: -::: zh-CN 写入以下内容,然后保存并退出: -::: -#### For version after v4.1.0 { lang="en" } - -#### v4.1.0 以后版本 { lang="zh-CN" } +#### v4.1.0 以后版本 ```yaml # docker-compose.yml @@ -598,9 +375,7 @@ services: # restart: unless-stopped ``` -#### For version v4.1.0 and earlier { lang="en" } - -#### v4.1.0 及以前版本 { lang="zh-CN" } +#### v4.1.0 及以前版本 ```yaml # docker-compose.yml @@ -709,12 +484,7 @@ services: # restart: unless-stopped ``` -::: en -Create `.env` file. -::: -::: zh-CN 创建 `.env` 文件。 -::: ```bash # ============================================================================= @@ -770,43 +540,14 @@ OPLISTDX_TEMP=./temp # OPLISTDX_TRANSMISSION_HOST_WHITELIST= ``` -::: en -Run commands in the same path of `docker-compose.yml` file: -::: -::: zh-CN 在 `docker-compose.yml` 相同目录下执行: -::: ```bash docker compose pull docker compose up -d ``` -### Env { lang="en" } - -### 环境变量 { lang="zh-CN" } - -::: en - -| Name | Default | Desc | -| :------------------------------------- | :------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `OPLISTDX_PUID` | `0` | User UID | -| `OPLISTDX_PGID` | `0` | User GID | -| `OPLISTDX_TZ` | `UTC` | Default is the UTC time zone. If you want to specify a time zone, you can set this variable, for example: `Asia/Shanghai`. | -| `OPLISTDX_DATA` | `./data` | Main data directory. | -| `OPLISTDX_TEMP` | `./temp` | Temporary files directory | -| `OPLISTDX_DOWNLOADS` | `./downloads` | Downloads directory | -| `OPLISTDX_TRANSMISSIONWATCH` | `./watch` | Transmission watch directory | -| `OPLISTDX_ARIA2TOKEN` | | Aria2 RPC secret token | -| `OPLISTDX_TRANSMISSION_WEB_HOME` | | Transmission Web UI theme directory | -| `OPLISTDX_TRANSMISSION_USER` | | Transmission Web UI username | -| `OPLISTDX_TRANSMISSION_PASS` | | Transmission Web UI password | -| `OPLISTDX_TRANSMISSION_WHITELIST` | | IP whitelist (comma separated) | -| `OPLISTDX_TRANSMISSION_PEERPORT` | | Peer port | -| `OPLISTDX_TRANSMISSION_HOST_WHITELIST` | | Host whitelist | - -::: -::: zh-CN +### 环境变量 | 名称 | 默认值 | 说明 | | :------------------------------------- | :------------ | -------------------------------------------------------------------------- | @@ -824,5 +565,3 @@ docker compose up -d | `OPLISTDX_TRANSMISSION_WHITELIST` | | IP 白名单(逗号分隔) | | `OPLISTDX_TRANSMISSION_PEERPORT` | | Peer 端口 | | `OPLISTDX_TRANSMISSION_HOST_WHITELIST` | | 主机白名单 | - -::: diff --git a/pages/guide/installation/download.md b/pages/guide/installation/download.md index 15fd68a45..2838bf126 100644 --- a/pages/guide/installation/download.md +++ b/pages/guide/installation/download.md @@ -1,19 +1,12 @@ --- -title: - en: Download - zh-CN: 下载 categories: - guide - installation top: 100 --- -::: en -You can download the corresponding binary executable file for the deployment system from this page or [GitHub Release](https://github.com/OpenListTeam/OpenList/releases). -::: +# 下载 -::: zh-CN 您可以在此页面或 [GitHub Release](https://github.com/OpenListTeam/OpenList/releases) 下载待部署系统对应的二进制可执行文件。 -::: diff --git a/pages/guide/installation/koyeb.md b/pages/guide/installation/koyeb.md index b4ad2d5e4..ef23a6f84 100644 --- a/pages/guide/installation/koyeb.md +++ b/pages/guide/installation/koyeb.md @@ -1,15 +1,12 @@ --- -title: - en: Koyeb - zh-CN: 使用 Koyeb -icon: iconfont icon-module top: 42 categories: - guide - installation --- -::: zh-CN +# 使用 Koyeb + ::: tip [Koyeb免费方案](https://www.koyeb.com/pricing#features)包含: @@ -18,67 +15,18 @@ categories: ::: -::: en -::: tip -The [Koyeb free plan](https://www.koyeb.com/pricing#features) includes: - -- 1 Web Service (500 MB RAM, regions: Germany 🇩🇪 or United States 🇺🇸) -- ~~1 Postgres~~ (Koyeb's free database only provides 5 hours per month; this guide uses the [Supabase free plan](https://supabase.com/pricing) instead) - -::: - -## Deployment Overview { lang="en" } - -::: en - -- Create a database on Supabase to persist OpenList configuration data -- Deploy the OpenList application on Koyeb - -::: - -## 部署概览 { lang="zh-CN" } - -::: zh-CN +## 部署概览 - 在Supabase中创建一个数据库用于保存openlist的配置数据 - 在Koyeb中部署openlist应用 -::: - -## Prerequisites { lang="en" } - -::: en - -- A [Supabase account](https://supabase.com/dashboard) (click to sign up) -- A [Koyeb account](https://app.koyeb.com/auth/signup) (click to sign up) - -::: - -## 部署前准备 { lang="zh-CN" } - -::: zh-CN +## 部署前准备 - 一个[Supabase账户](https://supabase.com/dashboard)(点击注册登录) - 一个[Koyeb账户](https://app.koyeb.com/auth/signup)(点击注册登录) -::: - -### Create the Database { lang="en" } - -::: en -Sign in to [Supabase](https://supabase.com/dashboard). - -1. Create a new project. -2. In **New project**, set the **Database password** and save it. Select **Central EU (Frankfurt)** as the region. - > Choose a region close to your Koyeb server — **Frankfurt** or **Washington D.C** is recommended. -3. Open the new project and click the **Connect** button to find the DB_HOST. - ![db_host](/img/koyeb/db_host.png) - -::: - -### 创建数据库 { lang="zh-CN" } +### 创建数据库 -::: zh-CN 登录[Supabase](https://supabase.com/dashboard) 1. 创建一个新项目 @@ -87,43 +35,14 @@ Sign in to [Supabase](https://supabase.com/dashboard). 3. 进入新建的project点击**Connect**按钮可查看DB_HOST ![db_host](/img/koyeb/db_host.png) -::: - -## Create the Service { lang="en" } - -::: en - -1. Sign in to the [Koyeb console](https://app.koyeb.com/) and click **Create App**. -2. Select **Docker** under Web service. -3. Set **Image** to `openlistteam/openlist:latest` and click **Next**. -4. Choose the **Free** instance under **CPU Eco 🌱** and click **Next**. - -::: - -## 创建服务 { lang="zh-CN" } - -::: zh-CN +## 创建服务 1. 登录 [Koyeb 控制台](https://app.koyeb.com/),点击 **Create App**。 2. 选择Web service中的 **Docker**。 3. 将 **Image** 设置为 `openlistteam/openlist:latest`,点击**Next**。 4. 选择**CPU Eco 🌱**中的**Free**服务器点击**Next** -::: - -### Configure Deployment { lang="en" } - -::: en - -1. Enable the **Override** toggle next to **Command**. -2. Enter `./openlist server --config /tmp/config.json` in the input box. - ::: warning - You must specify the config file path; otherwise you will get the error: the current user does not have write and/or execute permissions on the `./data` directory (`/opt/openlist/data`). - ::: - -### 设置Deployment { lang="zh-CN" } - -::: zh-CN +### 设置Deployment 1. 打开**Command**后的**Override**开关 2. 在框中输入 `./openlist server --config /tmp/config.json` @@ -133,78 +52,20 @@ Sign in to [Supabase](https://supabase.com/dashboard). ![deployment](/img/koyeb/deployment.png) -### Set up Environment Variables and Files { lang="en" } - -#### Add environment variables { lang="en" } - -::: en -| Key | Value | Purpose | -| ------------------------- | ---------------------------------- | ----------------------------------------------------------------------- | -| `TZ` | `Asia/Shanghai` (or any timezone) | Set the container timezone | -| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | -| `OPENLIST_ADMIN_PASSWORD` | Initial admin password | Strongly recommended; otherwise a random password is generated on every restart | - -![environment](/img/koyeb/environment.png) - -::: - -#### Add configuration file { lang="en" } - -::: en -Switch to the `Files` tab, click **Add file**, and set **Path** to `/tmp/config.json` (matching the path specified in the Command). - -Enter the following in **File content**: - -```json -{ - "force": false, - "jwt_secret": "random_generated", - "database": { - "type": "postgres", - "host": "replace_with_your_DB_HOST", - "port": 5432, - "user": "postgres", - "password": "replace_with_your_database_password", - "name": "postgres", - "db_file": "", - "table_prefix": "x_", - "ssl_mode": "require", - "dsn": "" - }, - "scheme": { - "address": "0.0.0.0", - "http_port": 5244 - }, - "temp_dir": "/tmp/temp", - "bleve_dir": "/tmp/bleve", - "log": { - "enable": false - } -} -``` - -![file](/img/koyeb/file.png) - -::: - -### 设置 Environment variables and files { lang="zh-CN" } +### 设置 Environment variables and files -#### 添加环境变量 { lang="zh-CN" } +#### 添加环境变量 -::: zh-CN -| Key | Value | 用途 | +| Key | Value | 用途 | | ------------------------- | ----------------------------- | ------------------------------------------ | -| `TZ` | `Asia/Shanghai`(或任意时区) | 设置容器时区 | -| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | -| `OPENLIST_ADMIN_PASSWORD` | admin的起始密码 | 强烈建议设置,不然每次服务重启都会随机生成 | +| `TZ` | `Asia/Shanghai`(或任意时区) | 设置容器时区 | +| `UMASK` | `022` | https://en.wikipedia.org/wiki/Umask | +| `OPENLIST_ADMIN_PASSWORD` | admin的起始密码 | 强烈建议设置,不然每次服务重启都会随机生成 | ![environment](/img/koyeb/environment.png) -::: - -#### 添加配置文件 { lang="zh-CN" } +#### 添加配置文件 -::: zh-CN 切换到`Files`,点击**Add file**,将**Path**设置为`/tmp/config.json`(与Command中指定的一致) **File content**中填入: @@ -239,37 +100,11 @@ Enter the following in **File content**: ![file](/img/koyeb/file.png) -::: - -### Configure Ports { lang="en" } - -::: en -Change the **Port** to `5244`. - -::: - -### 设置Ports { lang="zh-CN" } +### 设置Ports -::: zh-CN 将**Port**改为`5244` -::: - -## Deploy and Verify { lang="en" } - -::: en - -1. Review the summary and click **Save and deploy**. -2. After a short wait you should see OpenList initialization messages in the LOG panel. -3. Once deployment is complete, click the link provided by Koyeb to access OpenList. - -![success](/img/koyeb/success.png) - -::: - -## 部署与验证 { lang="zh-CN" } - -::: zh-CN +## 部署与验证 1. 核对摘要信息后点击 **Save and deploy**。 2. 稍等一会就能在LOG窗口中看到 OpenList 的初始化信息。 @@ -277,22 +112,7 @@ Change the **Port** to `5244`. ![success](/img/koyeb/success.png) -::: - -## Updates and Maintenance { lang="en" } - -::: en - -- **Updates**: Go to the Service detail page. If the image is set to `latest`, simply click **Redeploy** when a new version is available. -- **Logs**: View real-time logs on the Service detail page, or click **Details** under Scaling. - -::: - -## 更新与维护 { lang="zh-CN" } - -::: zh-CN +## 更新与维护 - **更新**:进入Service详情页,如果之前设置的镜像为latest,当有新版本时手动点击**Redeploy**即可更新版本 - **日志**:在Service详情页查看实时日志,或在Scaling中点击**Details**。 - -::: diff --git a/pages/guide/installation/manual.md b/pages/guide/installation/manual.md index 0b39813c0..93a9a7635 100644 --- a/pages/guide/installation/manual.md +++ b/pages/guide/installation/manual.md @@ -1,103 +1,26 @@ --- -title: - en: Manual installation - zh-CN: 手动安装 -icon: iconfont icon-interact -# This control sidebar order top: 60 -# A page can have multiple categories categories: - guide - installation -# A page can have multiple tags -tag: - - Install - - Guide -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -## Get OpenList { lang="en" } +# 手动安装 -## 获取 OpenList { lang="zh-CN" } +## 获取 OpenList -::: en -You can download the corresponding binary executable file for the deployment system from [Download](./download) page or [GitHub Release](https://github.com/OpenListTeam/OpenList/releases). -::: - -::: zh-CN 您可以在 [下载](./download) 页面或 [GitHub Release](https://github.com/OpenListTeam/OpenList/releases) 下载待部署系统对应的二进制可执行文件。 -::: [![latest version](https://img.shields.io/github/release/OpenListTeam/OpenList)](https://github.com/OpenListTeam/OpenList/releases) -## Install using package manager { lang="en" } - -## 使用包管理器安装 { lang="zh-CN" } +## 使用包管理器安装 ### Linux -::: en -Debian/Ubuntu can be installed from OpenList's APT repository and PPA repository, and the service (daemon) will be automatically configured. -::: -::: zh-CN Debian / Ubuntu 可以从 OpenList 的 APT 仓库和 PPA 仓库安装,且会自动配置好服务(守护进程)。 -::: - -#### APT Repository { lang="en" } - -#### APT 仓库 { lang="zh-CN" } - -::: en -Recommended - Automatic GPG Setup - -```bash -# One-line install with automatic GPG key setup -curl -fsSL https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/install-apt.sh | bash - -# Then install OpenList -sudo apt install openlist -y -``` -Manual APT Setup with GPG Verification (Modern systems - Ubuntu 22.04+/Debian 12+) +#### APT 仓库 -```bash -# Download and install GPG keyring -curl -fsSL https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/openlist-archive-keyring.gpg | \ - sudo tee /usr/share/keyrings/openlist-archive-keyring.gpg > /dev/null - -# Add repository with GPG verification -echo "Types: deb -URIs: https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/ -Suites: ./ -Signed-By: /usr/share/keyrings/openlist-archive-keyring.gpg" | \ - sudo tee /etc/apt/sources.list.d/openlist.sources - -# Update and install -sudo apt update && sudo apt install openlist -y -``` - -Manual APT Setup without GPG Verification (Not Recommended) - -```bash -# Modern systems (Ubuntu 22.04+/Debian 12+) -echo "Types: deb -URIs: https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/ -Suites: ./ -Trusted: yes" | sudo tee /etc/apt/sources.list.d/openlist.sources - -# Legacy systems (Ubuntu <22.04/Debian <12) -echo "deb [trusted=yes] https://github.com/OpenListTeam/OpenList-APT/releases/latest/download/ ./" | \ - sudo tee /etc/apt/sources.list.d/openlist.list - -# Update and install -sudo apt update && sudo apt install openlist -y -``` - -::: -::: zh-CN 推荐 - 自动 GPG 设置 ```bash @@ -143,26 +66,8 @@ echo "deb [trusted=yes] https://github.com/OpenListTeam/OpenList-APT/releases/la sudo apt update && sudo apt install openlist -y ``` -::: - -#### PPA Repository { lang="en" } - -#### PPA 仓库 { lang="zh-CN" } +#### PPA 仓库 -::: en -Alternative - Launchpad - -```bash -# Add PPA repository -sudo add-apt-repository ppa:openlist/server -sudo apt update - -# Install OpenList -sudo apt install openlist -y -``` - -::: -::: zh-CN 备用方法 - Launchpad ```bash @@ -174,56 +79,12 @@ sudo apt update sudo apt install openlist -y ``` -::: - -#### Flatpak { lang="en" } - -#### Flatpak { lang="zh-CN" } - -::: en -OpenList can be installed as a Flatpak package on most Linux distributions. Flatpak provides a sandboxed environment and automatic updates. +#### Flatpak -::: -::: zh-CN OpenList 可以在大多数 Linux 发行版上作为 Flatpak 软件包安装。Flatpak 提供沙盒环境和自动更新。 -::: - [![Flatpak package](https://img.shields.io/badge/dynamic/json?color=4A90E2&label=Flatpak&query=tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FOpenListTeam%2FOpenList-FLATPAK%2Freleases%2Flatest&logo=flatpak)](https://github.com/OpenListTeam/OpenList-FLATPAK/releases) -::: en -One-line Installation (Recommended) - -```bash -curl -fsSL https://github.com/OpenListTeam/OpenList-FLATPAK/releases/latest/download/install-flatpak.sh | bash -``` - -Manual Installation - -```bash -# Install Flatpak (if not already installed) -# On Ubuntu/Debian -sudo apt install flatpak - -# On Fedora -sudo dnf install flatpak - -# On Arch Linux -sudo pacman -S flatpak - -# Add Flathub repository (required for dependencies) -flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - -# Download and install OpenList Flatpak package -wget https://github.com/OpenListTeam/OpenList-FLATPAK/releases/latest/download/org.oplist.openlist-linux-x86_64.flatpak -flatpak install --user --bundle org.oplist.openlist-linux-x86_64.flatpak -y - -# Start OpenList -flatpak run org.oplist.openlist server -``` - -::: -::: zh-CN 一行命令安装(推荐) ```bash @@ -254,16 +115,9 @@ flatpak install --user --bundle org.oplist.openlist-linux-x86_64.flatpak -y flatpak run org.oplist.openlist server ``` -::: - ### Windows -::: en -OpenList can be installed from package managers Scoop main and WinGet on Windows. -::: -::: zh-CN Windows 可以从 Scoop main 和 WinGet 安装。 -::: #### Scoop @@ -283,12 +137,7 @@ openlist server ### macOS -::: en -OpenList is available via Homebrew on macOS. -::: -::: zh-CN macOS 可以从 Homebrew 安装。 -::: #### Homebrew @@ -300,28 +149,10 @@ $ brew install openlist ### Android -::: en -::: tip -OpenList follows the AGPL 3.0 open-source license, assumes no responsibility for any downstream derivative projects, and reserves the right to pursue their compliance with the same license. -::: - -::: zh-CN ::: tip OpenList 遵循 AGPL 3.0 开源协议,对任何下游衍生项目概不负责,且保留追究其同样遵守该协议的权利。 ::: -::: en -There are four ways to choose based on your needs - -1. Using **https://github.com/OpenListTeam/OpenList-Mobile** -2. Using **https://github.com/LeoHaoVIP/AListLiteAndroid** -3. Using **https://github.com/jing332/AListFlutter** (no longer maintained) -4. Use `termux` to run - - Reference: **https://anwen-anyi.github.io/index/14-android_install.html** - - Note: Remember to authorize the APP, set the background running and battery saving policy to unlimited, otherwise it may be killed in the background, causing it to be suddenly interrupted and unusable during background use. - -::: -::: zh-CN 有四种办法根据自己的需求选择 1. 使用 **https://github.com/OpenListTeam/OpenList-Mobile** @@ -331,8 +162,6 @@ There are four ways to choose based on your needs - 参考:**https://anwen-anyi.github.io/index/14-android_install.html** - 注意事项:记得给APP授权,后台运行、电池省电策略设置为无限制,否则可能会被杀后台导致挂在后台使用期间突然中断无法使用 -::: - #### Termux [![Termux package](https://repology.org/badge/version-for-repo/termux/openlist.svg)](https://repology.org/project/openlist/versions) @@ -343,47 +172,7 @@ pkg install openlist openlist server ``` -## Running { lang="en" } - -## 手动运行 { lang="zh-CN" } - -::: en - -```bash -Usage: - openlist [command] - -Available Commands: - admin Show admin user's info and some operations about admin user's password - cancel2fa Delete 2FA of admin user - completion Generate the autocompletion script for the specified shell - crypt Encrypt or decrypt local file or dir - help Help about any command - kill Force kill openlist server process by daemon/pid file - lang Generate language json file - restart Restart openlist server by daemon/pid file - server Start the server at the specified address - start Silent start openlist server with `--force-bin-dir` - stop Same as the kill command - storage Manage storage - version Show current version of OpenList - -Flags: - --data string data folder (default "data") - --config string config file (default "data/config.json") - --debug start with debug mode - --dev start with dev mode - --force-bin-dir Force to use the directory where the binary file is located as data directory - -h, --help help for openlist - --log-std Force to log to std - --no-prefix disable env prefix - -Use "openlist [command] --help" for more information about a command. -``` - -::: - -::: zh-CN +## 手动运行 ```bash 使用方法: @@ -417,43 +206,6 @@ Use "openlist [command] --help" for more information about a command. 使用 "openlist [命令] --help" 获取更多命令信息。 ``` -::: - -::: en -::: tip -If there is a prompt as follows:It is because [your GLIBC version is too low](../../faq/why#lib64-libc-so-6-version-glibc-2-28-not-found-required-by-openlist-or-accept-function-not-implemented), it is recommended to download the musl version. - -```txt -lib64/libc.so.6: version `GLIBC_2.28' not found (required by ./openlist) -accept: function not implemented -``` - -When you see the output of `start server @ 0.0.0.0:5244` and no error is reported afterwards, it means that the operation is successful. The initial password will be output when running for the first time. The program listens to port 5244 by default. Now open `http://ip:5244` You can see the login page, please see [WebDav](../advanced/webdav) for webdav. - -**For Flatpak Users** - -If you installed OpenList via Flatpak, use the following commands instead: - -```bash -# Start server -flatpak run org.oplist.openlist server - -# Show admin info -flatpak run org.oplist.openlist admin - -# Generate random admin password -flatpak run org.oplist.openlist admin random - -# Set admin password -flatpak run org.oplist.openlist admin set NEW_PASSWORD - -# Show version -flatpak run org.oplist.openlist version -``` - -::: - -::: zh-CN ::: tip 手动安装如果有如下提示:是因为[你的 GLIBC 版本太低](../../faq/why.md#lib64-libc-so-6-version-glibc-2-28-not-found-required-by-openlist-或者-accept-function-not-implemented),建议下载 musl 版本。 @@ -488,23 +240,11 @@ flatpak run org.oplist.openlist version
::: -::: en -::: warning -Versions above v3.25.0 change the password to an encrypted hash value, and the password cannot be calculated directly. If the password is forgotten, it can only be re-**`randomly generated`** or **`manually set`**. -::: - -::: zh-CN ::: warning v3.25.0以上版本将密码改成加密方式存储的hash值,无法直接反算出密码,如果忘记了密码只能通过重新 **`随机生成`** 或者 **`手动设置`**。 ::: -::: en -The `xxxx` refers to the names corresponding to different systems/architectures, generally Linux-x86/64 is openlist-linux-amd64. -::: - -::: zh-CN `xxxx` 指的是不同系统/架构对应的名称,一般 Linux-x86/64 为 openlist-linux-amd64。 -::: ### Linux @@ -538,18 +278,11 @@ Expand-Archive .\openlist-xxxx.zip .\openlist.exe admin set NEW_PASSWORD ``` -## Daemon { lang="en" } - -## 守护进程 { lang="zh-CN" } +## 守护进程 ### Linux -::: en -`vim /usr/lib/systemd/system/openlist.service` add the following content, where `path_openlist` is the path where openlist is located: -::: -::: zh-CN 使用任意方式编辑 `/usr/lib/systemd/system/openlist.service` 并添加如下内容,其中 `path_openlist` 为 OpenList 所在的路径: -::: ```ini [Unit] @@ -564,21 +297,6 @@ Restart=on-failure WantedBy=multi-user.target ``` -::: en -Then `systemctl daemon-reload`, now you can use these commands to manage the program: - -- Start: `systemctl start openlist` -- Shut down: `systemctl stop openlist` -- Self-start: `systemctl enable openlist` -- Cancel Self-start: `systemctl disable openlist` -- Status: `systemctl status openlist` -- Restart: `systemctl restart openlist` - -Can't configure daemon? [**Video Tutorial**](https://www.bilibili.com/video/BV1rF41197Qv?t=187.0) - -::: - -::: zh-CN 然后,执行 `systemctl daemon-reload` 重载配置,现在你可以使用这些命令来管理程序: - 启动: `systemctl start openlist` @@ -590,17 +308,9 @@ Can't configure daemon? [**Video Tutorial**](https://www.bilibili.com/video/BV1r 守护进程不会配置? [**视频教程**](https://www.bilibili.com/video/BV1rF41197Qv?t=187.0) -::: - ### macOS -::: en -Edit `~/Library/LaunchAgents/org.openlist.plist` in any way and add the following content, modify `path_openlist` to be the path where OpenList is located, and `path/to/working/dir` to be the working path of OpenList: -::: - -::: zh-CN 使用任意方式编辑 `~/Library/LaunchAgents/org.openlist.plist` 并添加如下内容,修改 `path_openlist` 为 OpenList 所在的路径,`path/to/working/dir` 为 OpenList的工作路径: -::: ```xml @@ -626,45 +336,15 @@ Edit `~/Library/LaunchAgents/org.openlist.plist` in any way and add the followin ``` -::: en -Then, execute `launchctl load ~/Library/LaunchAgents/org.openlist.plist` to load the configuration, now you can use these commands to manage the program: - -- Start: `launchctl start ~/Library/LaunchAgents/org.openlist.plist` -- Close: `launchctl stop ~/Library/LaunchAgents/org.openlist.plist` -- Unload configuration: `launchctl unload ~/Library/LaunchAgents/org.openlist.plist` - -::: - -::: zh-CN 然后,执行 `launchctl load ~/Library/LaunchAgents/org.openlist.plist` 加载配置,现在你可以使用这些命令来管理程序: - 开启: `launchctl start ~/Library/LaunchAgents/org.openlist.plist` - 关闭: `launchctl stop ~/Library/LaunchAgents/org.openlist.plist` - 卸载配置: `launchctl unload ~/Library/LaunchAgents/org.openlist.plist` -::: - ### Windows -#### Method One { lang="en" } - -#### 方法1 { lang="zh-CN" } - -::: en - -1. Download the newest `nssm` from https://nssm.cc/download. -2. Unzip the archive and go to the diretory of `nssm.exe`. -3. Hold Shift and right click on the blank space, then release and press S or select "powershell here", you should now see a blue window named "Windows PowerShell". -4. Type `.\nssm.exe install openlist`. -5. Select the path of `openlist.exe` for "Path", e.g. `D:\openlist\openlist.exe`; type `server` for "Argument". -6. You can custom "Display Name", "Description" and "Startup Type" in "Details" tab. -7. Go to "I/O" tab and select a file for both "Output (stdout)" and "Output (stderr)", e.g. `D:\openlist\stdout.log`. The file itself (`stdout.log`) may not exist, but the folder (`D:\openlist`) must exist. -8. Click on "Install Service". - You can now start the service from services.msc or task manager. - -::: - -::: zh-CN +#### 方法1 1. 在 https://nssm.cc/download 下载最新版本的 `nssm`; 2. 在解压后的文件夹内按住 Shift 并右击空白处,选择“在此处打开 Powershell 窗口”; @@ -675,49 +355,11 @@ Then, execute `launchctl load ~/Library/LaunchAgents/org.openlist.plist` to load 7. 点击“Install Service”即可。 此后可以直接在服务中启动 `openlist`。 -::: - -#### Method Two { lang="en" } +#### 方法2 -#### 方法2 { lang="zh-CN" } - -::: zh-CN 用 **`.VBS`** 脚本启动和停止,分别创建两个脚本 分别是 `启动.vbs` 和 `停止.vbs`。 直接在和 OpenList 启动程序同级文件夹里面双击启动即可,不用担心没有反应 直接去 浏览器访问即可。 -::: - -::: en -Use **`.VBS`** script to start and stop, create two scripts respectively `start.vbs` and `stop.vbs`. -Just double-click to start it in the folder at the same level as the OpenList startup program, don't worry about no response, just go to the browser to access it. -::: - -::: en -::: info Two startup scripts -**start.vbs** - -```bash title="vbscript" -Dim ws -Set ws = Wscript.CreateObject("Wscript.Shell") -ws.run "openlist.exe server",vbhide -Wscript.quit -``` -**stop.vbs** - -```bash title="vbscript" -Dim ws -Set ws = Wscript.CreateObject("Wscript.Shell") -ws.run "taskkill /f /im openlist.exe",0 -Wscript.quit -``` - -1. If the script will not be created, you can download it yourself: [**Script Download**](https://www.alipan.com/s/DHPMhRtKUzY/folder/63e0961eae317bd4d4d945cda69dbb00f9837fb7) -2. If the script will not be used, you can watch the video: [**reference video**](https://www.bilibili.com/video/BV1wWYTzdE4B) - How to realize Windows startup automatically, you can refer to the script mentioned above to use the video (second). - -::: - -::: zh-CN ::: info 两个启动脚本 **启动.vbs** @@ -743,19 +385,6 @@ Wscript.quit ::: -::: en -::: info -For all platform, you can use follow command to silent start, stop and restart. (v3.4.0 and later) - -```bash -openlist start -openlist stop -openlist restart -``` - -::: - -::: zh-CN ::: info 对于所有平台,您可以使用以下命令来静默启动、停止和重新启动。 (v3.4.0 及更高版本) @@ -767,14 +396,6 @@ openlist restart ::: -## How to update { lang="en" } - -## 如何更新 { lang="zh-CN" } +## 如何更新 -::: en -Download the new version of OpenList and replace the previous one. -::: - -::: zh-CN 下载新版 OpenList,把之前的替换了即可。 -::: diff --git a/pages/guide/installation/paas.md b/pages/guide/installation/paas.md index cc32decf4..268758d7f 100644 --- a/pages/guide/installation/paas.md +++ b/pages/guide/installation/paas.md @@ -1,101 +1,49 @@ --- -# This is the title of the article -# title: One-click Script -title: - en: PaaS - zh-CN: PaaS -icon: iconfont icon-module -# This control sidebar order top: 30 -# A page can have multiple categories categories: - guide - installation -# A page can have multiple tags --- -::: zh-CN +# PaaS + ::: tip 文档可能更新不及时,请根据关键信息结合平台当前部署界面进行调整 ::: -::: en -::: tip -The documentation may not be updated in a timely manner. Please make adjustments based on the key information and the current deployment interface of the platform. -::: - -## ClawCloud { lang="en" } - -## 爪云 { lang="zh-CN" } -### One-click deployment { lang="en" } +## 爪云 -### 一键部署 { lang="zh-CN" } +### 一键部署 -::: en -::: danger -Due to the removal of `PUID`/`PGID` environment variable support for better container specifications in [OpenList v4.1.1](https://github.com/OpenListTeam/OpenList/releases/tag/v4.1.1), ClawCloud platform will not be able to function properly due to lack of permission to write data (including using images or app in App Store). - -As of October 20 2025, ClawCloud has not yet merged our Deployment Template. One-click deployment is temporarily unavailable. If you want to deploy on ClawCloud, please refer to [#1209 (comment)](https://github.com/OpenListTeam/OpenList/issues/1209#issuecomment-3243803024). -::: -::: zh-CN ::: danger 由于在[OpenList v4.1.1](https://github.com/OpenListTeam/OpenList/releases/tag/v4.1.1)中为容器规范移除了`PUID`/`PGID`环境变量支持,故在 ClawCloud 平台将由于无权限写入数据而无法正常使用(包括使用镜像和App Store内应用) 截止 2025年10月20日, ClawCloud 官方暂未合并我们的修复部署模板,一键部署暂不可用。如需部署至ClawCloud,请参阅 [#1209 (comment)](https://github.com/OpenListTeam/OpenList/issues/1209#issuecomment-3243803024)。 ::: -::: en -Click the button below to [deploy to Claw Cloud](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Dopenlist). It can also be found in App Launchpad. -::: - -::: zh-CN 点击下方按钮[部署到 Claw Cloud](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Dopenlist)。亦可在 App Launchpad 中找到。 -::: [![Run on CLAWCLOUD](/img/guide/installation/clawcloud-run.svg)](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Dopenlist) -### Manual deployment { lang="en" } - -### 手动部署 { lang="zh-CN" } - -#### 关键信息 { lang="zh-CN" } - -#### Key Information { lang="en" } - -::: en -Name|Value|Description -:--|:--|:--- -Application Name|openlist|Application name -Image|openlistteam/openlist:latest-lite|Image, please make sure to use the one with **lite**, otherwise the error `Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.` may occur -Replicas|1|Number of replicas, set to 1 -CPU|0.2|Number of CPU cores, set according to your own needs -Memory|256M|Memory size, set according to your own needs -Container Port|5244|Mapped port, which is `5244` if you have not modified the startup command or configuration -Public Access|Y|Enable external access -Custom Domain|-|If you have your own domain name, set it according to the prompts; if not, keep the default. Please make sure to use **https** for the protocol -Local Storage|-|Persistent volume --- Capacity|1|Capacity --- Mount Path|/opt/openlist/data|Configured mapped directory, which is `/opt/openlist/data` if you have not modified the startup command or configuration -::: -::: zh-CN -名称|值|说明 -:--|:--|:--- -Application Name|openlist|应用名 -Image|openlistteam/openlist:latest-lite|镜像,请务必使用带有**lite**的镜像,否则可能出现`Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.` -Replicas|1|副本数,设置为1 -CPU|0.2|CPU核心数,请根据自身需求设置 -Memory|256M|内存大小,请根据自身需求设置 -Container Port|5244|映射端口,如果你没有修改启动命令或者配置,则为`5244` -Public Access|Y|打开外部访问 -Custom Domain|-|如果你有自身的域名,请根据提示进行设置,如果没有,保持默认,协议请务必使用**https** -Local Storage|-|持久卷 --- Capacity|1|容量 --- Mount Path|/opt/openlist/data|配置映射的目录,如果你没有修改启动命令或者配置,则为`/opt/openlist/data` -::: - -#### 参考图示 { lang="zh-CN" } - -#### Reference Diagram { lang="en" } +### 手动部署 + +#### 关键信息 + +| 名称 | 值 | 说明 | +| :--------------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | +| Application Name | openlist | 应用名 | +| Image | openlistteam/openlist:latest-lite | 镜像,请务必使用带有**lite**的镜像,否则可能出现`Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.` | +| Replicas | 1 | 副本数,设置为1 | +| CPU | 0.2 | CPU核心数,请根据自身需求设置 | +| Memory | 256M | 内存大小,请根据自身需求设置 | +| Container Port | 5244 | 映射端口,如果你没有修改启动命令或者配置,则为`5244` | +| Public Access | Y | 打开外部访问 | +| Custom Domain | - | 如果你有自身的域名,请根据提示进行设置,如果没有,保持默认,协议请务必使用**https** | +| Local Storage | - | 持久卷 | +| -- Capacity | 1 | 容量 | +| -- Mount Path | /opt/openlist/data | 配置映射的目录,如果你没有修改启动命令或者配置,则为`/opt/openlist/data` | + +#### 参考图示 ![](/img/guide/installation/clawcloud-01.png) diff --git a/pages/guide/installation/reverse-proxy.md b/pages/guide/installation/reverse-proxy.md index 9b017b223..ac5fad8f8 100644 --- a/pages/guide/installation/reverse-proxy.md +++ b/pages/guide/installation/reverse-proxy.md @@ -1,40 +1,11 @@ --- -# This is the title of the article -# title: One-click Script -title: - en: Reverse proxy - zh-CN: 反向代理 -icon: iconfont icon-page -# This control sidebar order top: 10 -# A page can have multiple categories categories: - guide - installation -# A page can have multiple tags -tag: - - Install - - Guide -# this page is sticky in article list -sticky: true -# this page will appear in starred articles -star: true --- -::: en -OpenList listens to port 5244 by default. If modified, please also update the port number in the configuration below. - -If you are using **reverse proxy**, please ensure that the correct `Host` header is passed, as OpenList will generate the URL based on this information. - -If the `Host` header is unavailable, you can use the higher-priority `X-Forwarded-Host` header (non-standard header). If the issue persists, configure the [site_url](../../configuration/configuration.md#site-url). - -If you want to use a **sub folder**, you should configure the [site_url](../../configuration/configuration.md#site-url). Refer to [reverse proxy with sub folder](../../faq/howto.md#how-to-reverse-proxy-with-sub-directory). - -:::warning -If you need to proxy to a non-standard port, make sure to pass the `domain:port` information through the `Host` or `X-Forwarded-Host` header, otherwise the port will be lost in URLs! -::: - -::: zh-CN +# 反向代理 OpenList 默认监听 5244 端口。如有修改,请一并修改下列配置中的端口号。 @@ -52,12 +23,7 @@ OpenList 默认监听 5244 端口。如有修改,请一并修改下列配置 https://nginx.org/en/docs/http/ngx_http_proxy_module.html -::: en -Add in the `server` field of the website configuration file: -::: -::: zh-CN 在网站配置文件的 `server` 字段中添加: -::: ```nginx location / { @@ -75,26 +41,6 @@ location / { } ``` -::: en -::: warning -If you use the aaPanel, be sure to delete the following default configuration: - -```nginx -location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md -location ~ .\*\.(gif|jpg|jpeg|png|bmp|swf)$ -location ~ .\*\.(js|css)?$ -``` - -Disable Nginx caching in `/www/server/nginx/conf/proxy.conf` or the corresponding website configuration file. Otherwise, with the default configuration, when accessing large files, Nginx will attempt to cache the remote file locally first, resulting in playback failures. - -```nginx -proxy_cache cache_one; # Remove this line -proxy_max_temp_file_size 0; # Add this line -``` - -::: - -::: zh-CN ::: warning 如果使用宝塔面板,请务必删除以下默认配置: @@ -113,20 +59,6 @@ proxy_max_temp_file_size 0; #加上这一行 ::: -::: en - -::: note Nginx Host Variables Differences - -| Variable | Description | Includes Port | Notes | -| -------------------- | ------------------------------------------------ | ------------- | ---------------------------------------------------------------------------------------------------- | -| `$http_host` | The original `Host` request header | Yes | If the `Host` request header is missing, the value will be empty; not recommended for best practices | -| `$host` | Server name from the `Host` request header field | No | Defaults to the server's `server_name` if `Host` field is missing | -| `$host:$server_port` | Server name + port | Yes | Use this combination when you need to use a non-default port (other than 80 or 443) | - -::: - -::: zh-CN - ::: tip Nginx 主机变量区别 | 变量 | 内容 | 是否包含端口号 | 备注 | @@ -137,18 +69,6 @@ proxy_max_temp_file_size 0; #加上这一行 ::: -::: en -If HTTP/3 is needed, the corresponding `Host` line should be modified to: - -```nginx -proxy_set_header Host $host:$server_port; -``` - -This modified configuration will be compatible with requests using HTTP/2 or lower versions. - -::: - -::: zh-CN 如果需要使用 HTTP/3,需要将对应 `Host` 行修改为: ```nginx @@ -157,27 +77,8 @@ proxy_set_header Host $host:$server_port; 这样修改后的配置同时也可以兼容 HTTP/2 及更低版本的请求。 -::: - ## Apache -::: en -Add the anti-generation configuration item `ProxyPass` under the `VirtualHost` field, such as: - -```xml - - ServerName myapp.example.com - ServerAdmin webmaster@example.com - DocumentRoot /www/myapp/public - AllowEncodedSlashes NoDecode - ProxyPreserveHost On - ProxyPass "/" "http://127.0.0.1:5244/" nocanon - ProxyPassReverse "/" "http://127.0.0.1:5244/" nocanon - -``` - -::: -::: zh-CN 在 `VirtualHost` 字段下添加配置项 `ProxyPass`,如: ```xml @@ -192,17 +93,9 @@ Add the anti-generation configuration item `ProxyPass` under the `VirtualHost` f ``` -::: - ## Caddy -::: en -Add the reverse proxy configuration item `reverse_proxy` under the `Caddyfile` file, for example: -::: - -::: zh-CN 在 `Caddyfile` 文件下添加 `reverse_proxy` 反向代理配置项,如: -::: ``` :80 { @@ -210,17 +103,9 @@ Add the reverse proxy configuration item `reverse_proxy` under the `Caddyfile` f } ``` -::: en -If deployed on a server that is functioning properly on port 443 and accessed using a domain name, it is recommended to use this configuration to let Caddy automatically request a certificate: - -(Replace example.com with your own resolved domain name) -::: - -::: zh-CN 如果部署在 443 端口正常使用的服务器上且使用域名进行访问,建议使用这种配置让 Caddy 自动申请证书: (将 `example.com` 替换为你自己解析后的域名) -::: ``` example.com { @@ -228,75 +113,21 @@ example.com { } ``` -## Tutorial: Setting up Reverse Proxy in aaPanel { lang="en" } - -## 教程:宝塔面板设置反向代理 { lang="zh-CN" } - -::: en - -1. Login to the aaPanel and add a new website. - -:: - -::: zh-CN - -1. 登录宝塔面板,添加站点; - -::: +## 教程:宝塔面板设置反向代理 ![bt_new_website](/img/guide/reverse_proxy/bt_new_website.png) -::: en - -2. Modify the website settings. - -::: - -::: zh-CN - 2. 修改站点设置; -::: - ![bt_new_website_01](/img/guide/reverse_proxy/bt_new_website_01.png) -::: en - -3. Remove the default panel code. - -::: - -::: zh-CN - 3. 删除面板默认代码; -::: - ![bt_delete_default_config_01](/img/guide/reverse_proxy/bt_delete_default_config_01.png) ![bt_delete_default_config_02](/img/guide/reverse_proxy/bt_delete_default_config_02.png) -::: en - -4. Add the reverse proxy. - -::: - -::: zh-CN - 4. 添加反向代理; -::: - ![bt_reverse_proxy](/img/guide/reverse_proxy/bt_reverse_proxy.png) -::: en - -> If you need to apply for an `SSL certificate`, you can first apply for the certificate in the `SSL` option, and then set up the reverse proxy. Alternatively, you can set up the reverse proxy first, disable the proxy function, apply for an `SSL` certificate, and then enable the proxy again. - -::: - -::: zh-CN - > 如需申请`SSL`证书,可先在`SSL`选项中申请证书,然后在设置反向代理。或者设置反向代理之后,关闭反向代理功能,申请`SSL`证书后再次开启代理。 - -::: diff --git a/pages/guide/installation/script.md b/pages/guide/installation/script.md index ce2b6dddf..d51d93b88 100644 --- a/pages/guide/installation/script.md +++ b/pages/guide/installation/script.md @@ -1,29 +1,12 @@ --- -# This is the title of the article -# title: One-click Script -title: - en: One-click Script - zh-CN: 一键脚本 -icon: iconfont icon-script -# This control sidebar order top: 70 -# A page can have multiple categories categories: - guide - installation --- -::: en -Requirements: +# 一键脚本 -- Linux with systemd or OpenRC -- Root privileges for installation -- `curl`, `tar` installed -- Architectures listed in the [download page](download) - -::: - -::: zh-CN 要求: - 使用 systemd 或者 OpenRC 的 Linux 系统 @@ -31,15 +14,9 @@ Requirements: - 已安装 `curl`, `tar` - 在[下载页面](download)中列出的架构 -::: - -## Latest { lang="en" } - -## 正式版 { lang="zh-CN" } +## 正式版 -### Install { lang="en" } - -### 安装 { lang="zh-CN" } +### 安装 ::: code-group @@ -87,24 +64,13 @@ curl -fsSL https://raw.githubusercontent.com/OpenListTeam/OpenList-Resource/refs 根据界面提示,输入`1`即可安装 -### openlist-manager { lang="en" } - -### 面板管理命令 { lang="zh-CN" } +### 面板管理命令 -::: zh-CN ::: tip **安装完成后才可使用** ::: -::: en -::: tip -**It can only be used after the installation is complete.** -::: -::: zh-CN + 使用命令:`openlist` 或者 `openlist-manager` -::: -::: en -Use command: `openlist` or `openlist-manager` -::: ```bash 欢迎使用 OpenList 管理脚本 @@ -134,22 +100,8 @@ Use command: `openlist` or `openlist-manager` 0、退出脚本 ``` -### FAQ { lang="en" } - -### 常见问题 { lang="zh-CN" } - -::: en - -1. Q: The architecture I am using is listed as supported on the download page, why does the installation script say it is not? - - A: This is because the installation script is currently unable to recognise your CPU architecture. To help us add CPU architecture recognition to the installation script, please submit the output of the `arch` and `uname -m` commands to the issue page. - -::: - -::: zh-CN +### 常见问题 1. Q:我使用的架构在下载页面支持的架构列表中,但安装脚本提示不支持? A:这是因为安装脚本暂时还没法识别您的CPU架构,请将`arch`和`uname -m`的输出信息提交到 issue 中,方便我们补充安装脚本的CPU架构识别。 - -::: diff --git a/pages/guide/installation/source.md b/pages/guide/installation/source.md index 1caab8a0f..9ec24e8bb 100644 --- a/pages/guide/installation/source.md +++ b/pages/guide/installation/source.md @@ -1,37 +1,19 @@ --- -# This is the title of the article -# title: One-click Script -title: - en: Run from source - zh-CN: 从源码运行 -icon: iconfont icon-code -# This control sidebar order top: 20 -# A page can have multiple categories categories: - guide - installation --- -::: en -:::danger -This step is only for users who need to modify the source code by themselves. If you don't understand, please skip it. -::: -::: zh-CN +# 从源码运行 + :::danger 此步骤仅适用于需要自行修改源代码的用户。不明白的请跳过。 ::: -## Environmental preparation { lang="en" } +## 环境准备 -## 环境准备 { lang="zh-CN" } - -::: en -First, you need to have an environment of `git`, `nodejs>=24`, `pnpm`, `golang>=1.24`, `gcc` -::: -::: zh-CN 首先,你需要一个有`git`,`nodejs>=24`,`pnpm`,`golang>=1.24`,`gcc`的环境 -::: On **Windows**, use Scoop: @@ -61,9 +43,7 @@ brew install node@24 go corepack enable pnpm ``` -## Building the frontend { lang="en" } - -## 构建前端 { lang="zh-CN" } +## 构建前端 ```bash git clone https://github.com/OpenListTeam/OpenList-Frontend.git @@ -71,16 +51,9 @@ cd OpenList-Frontend pnpm install && pnpm build ``` -## Build the backend { lang="en" } - -## 构建后端 { lang="zh-CN" } +## 构建后端 -::: en -Clone `https://github.com/OpenListTeam/OpenList` for this project, copy the `dist` directory of the previous step to the `public` directory under the project, and then: -::: -::: zh-CN 克隆 `https://github.com/OpenListTeam/OpenList` ,将上一步的 `dist` 目录复制到项目下的 `public` 目录下,然后执行: -::: ```bash appName="openlist" @@ -105,13 +78,6 @@ ldflags="\ go build -ldflags="$ldflags" . ``` -::: en -::: details compilation tutorial videos you may need -Windows version: **https://www.bilibili.com/video/BV1Xr4y1z723** (Although it is V2 version, it is the same as V3 version..) -Linux version: **https://www.bilibili.com/video/BV1GW4y1s742** -Compile documents: **https://www.yuque.com/anwenya/alist/glqlhu** -::: -::: zh-CN ::: details 你可能需要的编译教程视频 Windows版本:**https://www.bilibili.com/video/BV1Xr4y1z723** (虽然是V2版的,但是跟V3版本一样的..) @@ -119,44 +85,25 @@ Linux版本:**https://www.bilibili.com/video/BV1GW4y1s742** 番外编译文档:**https://www.yuque.com/anwenya/alist/glqlhu** ::: -## Building within Docker { lang="en" } - -## 使用 Docker 构建 { lang="zh-CN" } +## 使用 Docker 构建 -::: en -Install Docker, clone the repository, then navigate to the root directory of the repository. No further preparation is required. -::: -::: zh-CN 安装 Docker,克隆仓库后进入仓库根目录,无需其他准备 -::: - -#### Basic { lang="en" } -#### 基础用法 { lang="zh-CN" } +#### 基础用法 ```bash docker build -t openlistteam/openlist:beta . ``` -#### build-arg { lang="en" } - -#### 构建参数 { lang="zh-CN" } +#### 构建参数 ```bash docker build -t openlistteam/openlist:beta-ffmpeg --build-arg INSTALL_FFMPEG=true . ``` -::: en -Available build args: -| | Desc | -|:----------------------|----------------| -| `INSTALL_FFMPEG=true` | Install ffmpeg | -| `INSTALL_ARIA2=true` | Install aria2 | -::: -::: zh-CN 可用 build args: -| | 说明 | + +| | 说明 | | :-------------------- | ----------- | | `INSTALL_FFMPEG=true` | 安装 ffmpeg | -| `INSTALL_ARIA2=true` | 安装 aria2 | -::: +| `INSTALL_ARIA2=true` | 安装 aria2 | diff --git a/pages/guide/installation/truenas.md b/pages/guide/installation/truenas.md index 5f76b34a2..552e6d628 100644 --- a/pages/guide/installation/truenas.md +++ b/pages/guide/installation/truenas.md @@ -1,102 +1,14 @@ --- -title: - en: Use TrueNAS Scale - zh-CN: 使用 TrueNAS Scale -icon: iconfont icon-geometry -# This control sidebar order top: 44 -# A page can have multiple categories categories: - guide - installation --- -## Install the App { lang="en" } +# 使用 TrueNAS Scale -## 安装应用程序 { lang="zh-CN" } +## 安装应用程序 -::: en -Since OpenList is not officially available in the TrueNAS Apps catalog, you must use the **Custom App** feature to install it. - -Follow these steps to deploy OpenList via the **Install iX App** wizard: - -1. **Application Name** - Enter a name for your application (e.g., `openlist`). Keep the default version unless you require a specific tag. - - ![Application Name](/img/truenas/InstallCustomAppApplicationName.png) - -2. **Image Configuration** - - **Repository**: `openlistteam/openlist` - - **Tag**: `latest` (or specify another version if needed) - Leave all other fields at their defaults. - - ![Image Configuration](/img/truenas/InstallCustomAppImageConfiguration.png) - -3. **Container Configuration** - - **Environment Variables**: - Add a new variable: - - Name: `UMASK` - - Value: `022` - - **Restart Policy**: Select `Unless Stopped` to ensure automatic restart on failure. - - **Entrypoint**: Leave unchanged — it is pre-configured. - Other settings may be adjusted as desired. - - ![Container Configuration](/img/truenas/InstallCustomAppContainerEntrypoint.png) - -4. **Device** - No device passthrough is required for basic operation. Keep defaults. - -5. **Security Context Configuration** - - ✅ Check **Custom User** - - Set **UID** and **GID** to match a non-root user with permissions to access your storage volumes. - Default: `568/568` (apps/apps) — recommended for security. - - ⚠️ Avoid using `root` (UID/GID = 0) unless absolutely necessary — it poses a security risk. - > 💡 If you plan to use an **ixVolume** later, ensure this user has write permissions to the target dataset. - - ![Security Context Configuration](/img/truenas/InstallCustomAppSecurityContextConfiguration.png) - -6. **Network Configuration** - - Add a port mapping: - - **Host Port**: Any unused port (e.g., `10544`) - - **Container Port**: `5244` - - **Host IP**: `0.0.0.0` (accessible from any network) or restrict to a local IP if preferred. - Add additional mappings only if exposing other services. - - ![Network Configuration](/img/truenas/InstallCustomAppNetworkConfiguration.png) - -7. **Portal Configuration** - - **Port**: Match the Host Port from Step 6 (e.g., `10544`) - - **Name**: Optional — set to something descriptive like “OpenList Web UI” - Once saved, a **Web UI** button will appear on the Apps page. - - ![Portal Configuration](/img/truenas/InstallCustomAppPortalConfiguration.png) - -8. **Storage Configuration** - - ✅ **Mandatory**: At least one volume must be configured. - - Click **Add** → Select storage type (`ixVolume` recommended for persistence). - - **Mount Path**: `/opt/openlist/data` - - Ensure the container’s user (from Step 5) has **write permissions** to this location. - - Optionally add more volumes for logs, configs, etc. - - > 📌 Tip: Use **ixVolume** instead of host paths when possible — it ensures better integration with TrueNAS backup and snapshot features. - - ![Storage Configuration](/img/truenas/InstallCustomAppStorageConfiguration.png) - -9. **Labels Configuration** - Skip — not required for basic operation. - -10. **Resources Configuration** _(Optional)_ - Set CPU/memory limits if you wish to constrain resource usage (e.g., 1 core, 512MB RAM). - - ![Resources Configuration](/img/truenas/InstallCustomResourcesConfiguration.png) - -11. **Final Step** - Click **Install** to deploy the OpenList container. - Wait for status to turn green → then click **Web UI** to access the interface. - -::: - -::: zh-CN 由于 OpenList 未在 TrueNAS 应用市场官方提供,您必须使用 **自定义应用** 功能进行安装。 请按以下步骤通过 **安装 iX App** 向导部署 OpenList: @@ -172,7 +84,5 @@ Follow these steps to deploy OpenList via the **Install iX App** wizard: ![资源配置](/img/truenas/InstallCustomResourcesConfiguration.png) 11. **完成安装** - 点击 **安装** 启动容器。 - 等待状态变为绿色后,点击 **Web UI** 按钮即可访问 OpenList 界面。 - -::: + 点击 **安装** 启动容器。 + 等待状态变为绿色后,点击 **Web UI** 按钮即可访问 OpenList 界面。 diff --git a/pages/guide/installation/worker.md b/pages/guide/installation/worker.md index 615a03d59..566c5d131 100644 --- a/pages/guide/installation/worker.md +++ b/pages/guide/installation/worker.md @@ -1,207 +1,68 @@ --- -title: - en: OpenList Worker - zh-CN: OpenList Worker -icon: iconfont icon-cloud -# This control sidebar order top: 40 -# A page can have multiple categories categories: - guide - installation -tag: - - Worker - - Cloudflare - - EdgeOne - - Install --- -## What is OpenList Worker { lang="en" } +# OpenList Worker -## OpenList Worker 是什么 { lang="zh-CN" } +## OpenList Worker 是什么 -::: en -[**OpenList Worker**](https://github.com/OpenListTeam/OpenList-Worker)(仓库 `OpenList-TSWorker`)是 OpenList 官方的 TypeScript + Serverless 移植版。后端由 Go 重写为运行于边缘平台上的 TypeScript 服务,前端复用官方 [OpenList-Frontend](https://github.com/OpenListTeam/OpenList-Frontend)(SolidJS),无需自备服务器即可一键部署。 - -- **后端**:Hono.js(TypeScript) -- **运行平台**:Cloudflare Workers / EdgeOne Functions / Alibaba Cloud ESA / Vercel / AWS Lambda -- **数据库**:Cloudflare D1(SQLite)、MySQL / MariaDB / PostgreSQL / SQL Server、Cloudflare KV -- **许可证**:AGPL-3.0 - ::: - -::: zh-CN [**OpenList Worker**](https://github.com/OpenListTeam/OpenList-Worker)(仓库 `OpenList-TSWorker`)是 OpenList 官方的 TypeScript + Serverless 移植版。后端由 Go 重写为运行于边缘平台上的 TypeScript 服务,前端复用官方 [OpenList-Frontend](https://github.com/OpenListTeam/OpenList-Frontend)(SolidJS),无需自备服务器即可一键部署。 - **后端**:Hono.js(TypeScript) - **运行平台**:Cloudflare Workers / EdgeOne Functions / 阿里云 ESA / Vercel / AWS Lambda - **数据库**:Cloudflare D1(SQLite)、MySQL / MariaDB / PostgreSQL / SQL Server、Cloudflare KV - **许可证**:AGPL-3.0 - ::: - -## Supported Platforms { lang="en" } - -## 支持平台 { lang="zh-CN" } - -::: en -| Platform | Persistence | One-click | Notes | -| :--- | :--- | :---: | :--- | -| Cloudflare Workers | D1 / KV / JSON | ✅ | Recommended, global edge network | -| EdgeOne Makers(国际站 / 中国站) | EdgeOne Blob / KV | ✅ | Tencent Cloud edge platform | -| Alibaba Cloud ESA | EdgeKV | ✅ | Alibaba edge function | -| Vercel | In-memory(JSON) | — | No persistence by default | -| AWS Lambda(Serverless Framework) | In-memory(JSON) | — | `serverless.yml` | -::: - -::: zh-CN -| 平台 | 持久化 | 一键部署 | 说明 | -| :--- | :--- | :---: | :--- | -| Cloudflare Workers | D1 / KV / JSON | ✅ | 推荐,全球边缘网络 | -| EdgeOne Makers(国际站 / 中国站) | EdgeOne Blob / KV | ✅ | 腾讯云边缘平台 | -| 阿里云 ESA | EdgeKV | ✅ | 阿里云边缘函数 | -| Vercel | 内存(JSON) | — | 默认无持久化 | -| AWS Lambda(Serverless Framework) | 内存(JSON) | — | `serverless.yml` | -::: -## One-click Deploy { lang="en" } +## 支持平台 -## 一键部署 { lang="zh-CN" } +| 平台 | 持久化 | 一键部署 | 说明 | +| :--------------------------------- | :---------------- | :------: | :----------------- | +| Cloudflare Workers | D1 / KV / JSON | ✅ | 推荐,全球边缘网络 | +| EdgeOne Makers(国际站 / 中国站) | EdgeOne Blob / KV | ✅ | 腾讯云边缘平台 | +| 阿里云 ESA | EdgeKV | ✅ | 阿里云边缘函数 | +| Vercel | 内存(JSON) | — | 默认无持久化 | +| AWS Lambda(Serverless Framework) | 内存(JSON) | — | `serverless.yml` | -::: en -Click the button below to deploy to the corresponding platform: -::: +## 一键部署 -::: zh-CN 点击下方按钮,即可将本项目一键部署到对应平台: -::: | EdgeOne Makers · 国际站 | EdgeOne Makers · 中国站 | Cloudflare Workers · 全球站 | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![使用 EdgeOne 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://console.cloud.tencent.com/edgeone/pages/new?project-name=openlist-tsworker&repository-url=https://github.com/OpenListTeam/OpenList-Worker&install-command=pnpm%20install%20--no-frozen-lockfile&build-command=pnpm%20run%20build&output-directory=dist&env=JWT_SECRET) | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/OpenListTeam/OpenList-Worker) | -::: en - -> If Cloudflare prompts `无法获取存储库内容`(cannot fetch repository content), fork this project first, then deploy by connecting to the GitHub repository. -> ::: - -::: zh-CN - > 若 Cloudflare 提示 `无法获取存储库内容`,则您需要先 Fork 本项目,再通过连接到 GitHub 仓库功能部署。 -> ::: -## Initialization { lang="en" } +## 部署后初始化 -## 部署后初始化 { lang="zh-CN" } - -::: en -::: tip -After deployment, the first visit to the site automatically enters an **installation wizard**. Set the admin account and password in the browser to complete initialization — no pre-configured `ADMIN_PASS` is required. -::: - -::: zh-CN ::: tip 部署完成后,首次访问站点会自动进入**安装向导**,在浏览器中设置管理员账号与密码即可完成初始化,无需预先配置 `ADMIN_PASS`。 ::: -## Environment Variables { lang="en" } - -## 环境变量 / Secrets { lang="zh-CN" } - -::: en -| Variable | Required | Default | Description | -| :--- | :--- | :--- | :--- | -| `JWT_SECRET` | Recommended | — | JWT signing key, also used for data encryption and cron task auth. Auto-generated and persisted to KV when unset | -| `DB_FORMAT` | Optional | `map` | Storage format: `map` (whole JSON) / `key` (per-key) / `sql` (relational, Go-compatible) | -| `DB_DRIVER` | Optional | `auto` | Database driver: `auto` / `blob` / `cfkv` / `kv` / `d1` / `do` / `mysql` | -| `CF_ACCOUNT` | Optional | — | Cloudflare account ID (required for `cfkv` mode) | -| `CF_KV_UUID` | Optional | — | Cloudflare KV namespace ID (required for `cfkv` mode) | -| `CF_API_KEY` | Optional | — | Cloudflare API token (required for `cfkv` mode) | -| `MYSQL_URL` | Optional | — | MySQL connection string (or use `MYSQL_*` fields below) | -| `MYSQL_HOST` / `MYSQL_PORT` / `MYSQL_USER` / `MYSQL_PASS` / `MYSQL_NAME` | Optional | — | MySQL connection fields (`DB_DRIVER = mysql`) | -| `ADMIN_PASS` | Optional | — | Skip the install wizard and auto-initialize admin with this password | -| `ALLOW_URLS` | Optional | — | Comma-separated CORS allowlist | -| `MAX_UPLOAD` | Optional | `26214400` | Max size (bytes) for a whole upload (`/put`, `/form`) | -| `MAX_UPPART` | Optional | `16777216` | Max size (bytes) per chunk in multipart upload | -| `ASSET_URLS` | Optional | — | Frontend asset CDN base URL, supports the `$version` placeholder | -| `ALLOW_SEED` | Optional | — | Allowlist of hosts permitted as seed-data sources | -::: - -::: zh-CN -| 变量 | 必要 | 默认值 | 说明 | -| :--- | :--- | :--- | :--- | -| `JWT_SECRET` | 推荐 | — | JWT 签名密钥,同时用于数据加密与定时任务鉴权。未配置时自动生成并持久化到 KV | -| `DB_FORMAT` | 可选 | `map` | 数据存储格式:`map`(整对象 JSON)/ `key`(分 key 存储)/ `sql`(关系表,与 Go 后端一致) | -| `DB_DRIVER` | 可选 | `auto` | 数据库驱动:`auto` / `blob` / `cfkv` / `kv` / `d1` / `do` / `mysql` | -| `CF_ACCOUNT` | 可选 | — | Cloudflare 账号 ID(`cfkv` 模式必填) | -| `CF_KV_UUID` | 可选 | — | Cloudflare KV namespace ID(`cfkv` 模式必填) | -| `CF_API_KEY` | 可选 | — | Cloudflare API token(`cfkv` 模式必填) | -| `MYSQL_URL` | 可选 | — | MySQL 连接串(或用下方 `MYSQL_*` 分项) | -| `MYSQL_HOST` / `MYSQL_PORT` / `MYSQL_USER` / `MYSQL_PASS` / `MYSQL_NAME` | 可选 | — | MySQL 分项连接配置(`DB_DRIVER = mysql`) | -| `ADMIN_PASS` | 可选 | — | 跳过安装向导,以该密码自动初始化 admin | -| `ALLOW_URLS` | 可选 | — | CORS 允许白名单(逗号分隔) | -| `MAX_UPLOAD` | 可选 | `26214400` | 单次整体上传(`/put`、`/form`)大小上限(字节) | -| `MAX_UPPART` | 可选 | `16777216` | 分片上传单片大小上限(字节) | -| `ASSET_URLS` | 可选 | — | 前端静态资源 CDN 地址,支持 `$version` 占位符 | -| `ALLOW_SEED` | 可选 | — | 允许作为种子数据来源的主机白名单 | -::: - -## Data Backend(DB_FORMAT & DB_DRIVER) { lang="en" } - -## 数据存储后端(DB_FORMAT 与 DB_DRIVER) { lang="zh-CN" } - -::: en -OpenList Worker separates persistence into two orthogonal layers: - -- **`DB_FORMAT`** — how data is serialized and stored -- **`DB_DRIVER`** — which underlying storage system is used - -### `DB_FORMAT`(data storage format) - -| Value | Description | -| :--------------- | :-------------------------------------------------------------------------- | -| `map`(default) | Whole object serialized as a single JSON value, ideal for KV / Blob storage | -| `key` | Per-key storage, one record per entity (avoids large JSON) | -| `sql` | Relational tables, fully compatible with the Go backend (for D1 / MySQL) | - -### `DB_DRIVER`(database driver) - -| Value | Description | Suitable platform | -| :---------------- | :---------------------------------------------------------------------------- | :------------------------ | -| `auto`(default) | Auto-detect available driver(priority: blob → cfkv → kv → d1 → memory) | Universal, works anywhere | -| `blob` | Tencent EdgeOne Blob / Alibaba ESA Blob | EdgeOne / ESA | -| `cfkv` | Cloudflare KV REST API(requires `CF_ACCOUNT` / `CF_KV_UUID` / `CF_API_KEY`) | External / cross-account | -| `kv` | Cloudflare KV binding | Cloudflare Workers | -| `d1` | Cloudflare D1 (SQLite) | Cloudflare Workers | -| `do` | Cloudflare Durable Objects (SQLite) | Cloudflare Workers | -| `mysql` | External MySQL | Node.js container runtime | - -### Recommended combinations +## 环境变量 / Secrets + +| 变量 | 必要 | 默认值 | 说明 | +| :----------------------------------------------------------------------- | :--- | :--------- | :---------------------------------------------------------------------------------------- | +| `JWT_SECRET` | 推荐 | — | JWT 签名密钥,同时用于数据加密与定时任务鉴权。未配置时自动生成并持久化到 KV | +| `DB_FORMAT` | 可选 | `map` | 数据存储格式:`map`(整对象 JSON)/ `key`(分 key 存储)/ `sql`(关系表,与 Go 后端一致) | +| `DB_DRIVER` | 可选 | `auto` | 数据库驱动:`auto` / `blob` / `cfkv` / `kv` / `d1` / `do` / `mysql` | +| `CF_ACCOUNT` | 可选 | — | Cloudflare 账号 ID(`cfkv` 模式必填) | +| `CF_KV_UUID` | 可选 | — | Cloudflare KV namespace ID(`cfkv` 模式必填) | +| `CF_API_KEY` | 可选 | — | Cloudflare API token(`cfkv` 模式必填) | +| `MYSQL_URL` | 可选 | — | MySQL 连接串(或用下方 `MYSQL_*` 分项) | +| `MYSQL_HOST` / `MYSQL_PORT` / `MYSQL_USER` / `MYSQL_PASS` / `MYSQL_NAME` | 可选 | — | MySQL 分项连接配置(`DB_DRIVER = mysql`) | +| `ADMIN_PASS` | 可选 | — | 跳过安装向导,以该密码自动初始化 admin | +| `ALLOW_URLS` | 可选 | — | CORS 允许白名单(逗号分隔) | +| `MAX_UPLOAD` | 可选 | `26214400` | 单次整体上传(`/put`、`/form`)大小上限(字节) | +| `MAX_UPPART` | 可选 | `16777216` | 分片上传单片大小上限(字节) | +| `ASSET_URLS` | 可选 | — | 前端静态资源 CDN 地址,支持 `$version` 占位符 | +| `ALLOW_SEED` | 可选 | — | 允许作为种子数据来源的主机白名单 | + +## 数据存储后端(DB_FORMAT 与 DB_DRIVER) -```bash -# Cloudflare Workers + D1 (recommended, SQL format compatible with Go backend) -DB_FORMAT=sql -DB_DRIVER=d1 -``` - -> **Backward compatibility:** the legacy `DB_DRIVER=json` auto-converts to `DB_FORMAT=map` + auto-detected driver. - -#### Table naming (SQL format) - -The `sql` format uses columnar tables with the same naming strategy as the Go backend's GORM: snake*case + pluralized table names, plus the fixed `x*` prefix. - -| Go struct | Table name | -| :------------ | :---------------- | -| `SettingItem` | `x_setting_items` | -| `SharingDB` | `x_sharing_dbs` | -| `Storage` | `x_storages` | -| `User` | `x_users` | -| `Meta` | `x_metas` | -| _(TS only)_ | `x_plugins` | - -The prefix is fixed to `x_` (matching the Go backend default), so no extra configuration is needed to share the same physical database with the Go backend. - -::: - -::: zh-CN OpenList Worker 将持久化拆分为两个正交的层: - **`DB_FORMAT`** — 决定数据的序列化与存储方式 @@ -252,53 +113,7 @@ DB_DRIVER=d1 前缀固定为 `x_`(与 Go 后端默认值一致),无需额外配置即可与 Go 后端共享同一物理数据库。 -::: - -## Deploy to Cloudflare Workers { lang="en" } - -## 部署到 Cloudflare Workers { lang="zh-CN" } - -::: en - -### Prerequisites - -- A [Cloudflare](https://dash.cloudflare.com/) account -- Node.js 18+ and pnpm - -### Option 1: One-click deploy - -Click the **Deploy to Cloudflare Workers** button above. If it prompts that the repository cannot be fetched, fork the project first and deploy via the GitHub repository connection. - -### Option 2: Manual deploy with Wrangler - -```bash -# 1. Clone and install dependencies -git clone https://github.com/OpenListTeam/OpenList-Worker.git -cd OpenList-Worker -pnpm install - -# 2. Configure wrangler.jsonc (JWT_SECRET, KV / D1 bindings) - -# 3. Deploy to Cloudflare Workers -pnpm run deploy:worker -# or: pnpm run deploy (builds frontend + deploys backend) -``` - -### D1 database - -Use Cloudflare's _Automatic resource provisioning_: omit `database_id` in `wrangler.d1.jsonc`, and wrangler (>= 4.45.0) auto-creates a D1 database with the same name and writes back the ID on deploy: - -```jsonc -{ - "vars": { "DB_FORMAT": "sql", "DB_DRIVER": "d1" }, - "d1_databases": [{ "binding": "DB", "database_name": "openlist-data-base" }], -} -``` - -After deployment, configure the KV namespace binding and environment variables in the [Worker dashboard](https://dash.cloudflare.com/). -::: - -::: zh-CN +## 部署到 Cloudflare Workers ### 前置要求 @@ -336,47 +151,8 @@ pnpm run deploy:worker ``` 部署完成后,请在 [Worker 后台](https://dash.cloudflare.com/) 配置 KV namespace 绑定与环境变量。 -::: - -## Deploy to EdgeOne { lang="en" } - -## 部署到 EdgeOne { lang="zh-CN" } - -::: en - -### One-click deploy - -Click the **EdgeOne** deploy button above, choose the international or China site: -- [International console](https://console.edgeone.ai/makers) -- [China console](https://console.cloud.tencent.com/edgeone/makers) - -### Persistence - -EdgeOne Makers uses `@edgeone/pages-blob` for persistence. The default `auto` driver auto-detects Blob. You can also explicitly set `DB_DRIVER = "blob"` (with `DB_FORMAT = "map"`) or `DB_DRIVER = "kv"` (with `DB_FORMAT = "key"`). - -### Scheduled tasks - -EdgeOne supports scheduled refresh via `edgeone.json`. Set `cron_secret` in the payload to your `JWT_SECRET` value, and configure the schedule: - -```jsonc -{ - "schedules": [ - { - "name": "token-refresh", - "cron": "0 2 * * *", - "path": "/api/task/refresh", - "method": "POST", - "payload": { "cron_secret": "" }, - "timezone": "Asia/Shanghai", - }, - ], -} -``` - -::: - -::: zh-CN +## 部署到 EdgeOne ### 一键部署 @@ -408,47 +184,8 @@ EdgeOne 通过 `edgeone.json` 支持定时刷新。将 payload 中的 `cron_secr } ``` -::: - -## Deploy to Alibaba Cloud ESA { lang="en" } - -## 部署到阿里云 ESA { lang="zh-CN" } - -::: en -OpenList Worker ships a dedicated ESA edge function entry (`esa-entry.ts`), which adapts Alibaba Cloud EdgeKV into the project's KV interface. - -### Build & deploy - -```bash -# 1. Install dependencies -pnpm install - -# 2. Build (produces dist/esa-entry.js) -pnpm run build -``` - -`esa.jsonc` defines the edge function entry, install/build commands, and static assets directory: - -```jsonc -{ - "name": "openlist", - "entry": "./dist/esa-entry.js", - "installCommand": "pnpm install --no-frozen-lockfile", - "buildCommand": "pnpm run build", - "assets": { "directory": "./dist" }, -} -``` +## 部署到阿里云 ESA -### KV namespace - -Configure the EdgeKV namespace via the `KV_NAMESPACE` environment variable (default `openlist`). - -::: tip -ESA EdgeKV is eventually consistent. The entry implements a module-level TTL cache (60s) to avoid "saved settings revert after refresh" caused by cross-node sync delay. -::: -::: - -::: zh-CN OpenList Worker 内置了专用的 ESA 边缘函数入口(`esa-entry.ts`),将阿里云 EdgeKV 适配为项目的 KV 接口。 ### 构建与部署 @@ -479,29 +216,9 @@ pnpm run build ::: tip ESA EdgeKV 是最终一致性的。入口实现了带 TTL(60 秒)的模块级缓存,避免跨节点同步延迟导致的「保存设置后刷新复原」问题。 -::: -::: - -## Local Development { lang="en" } - -## 本地开发 { lang="zh-CN" } - -::: en - -```bash -# 1. Install backend dependencies -pnpm install - -# 2. Fetch frontend and run the backend (unified dev server) -pnpm run dev:unified - -# Or run the worker directly (frontend built separately) -pnpm run dev:worker -``` - ::: -::: zh-CN +## 本地开发 ```bash # 1. 安装后端依赖 @@ -514,49 +231,15 @@ pnpm run dev:unified pnpm run dev:worker ``` -::: - -## Production Deploy { lang="en" } - -## 生产部署 { lang="zh-CN" } - -::: en - -```bash -# One-click deploy (frontend build + backend deploy to Cloudflare Workers) -pnpm run deploy -``` - -::: - -::: zh-CN +## 生产部署 ```bash # 一键部署(前端构建 + 后端部署到 Cloudflare Workers) pnpm run deploy ``` -::: - -## Troubleshooting { lang="en" } +## 常见问题 -## 常见问题 { lang="zh-CN" } - -::: en -::: details Cloudflare prompts "cannot fetch repository content" -Fork the project first, then deploy by connecting to the GitHub repository instead of the direct one-click URL. -::: - -::: details Settings revert after save (ESA / EdgeOne) -This is usually a KV/CDN cache consistency issue. The entry already forces `no-cache` on `/api/*` GET responses and implements a module-level KV cache. If it persists, check that your KV namespace is correctly bound and not read-only. -::: - -::: details How do I reset the admin password? -The admin password is set during the install wizard. To reset, you can set `ADMIN_PASS` temporarily and redeploy, or clear the persisted config and re-run the wizard. -::: -::: - -::: zh-CN ::: details Cloudflare 提示「无法获取存储库内容」 请先 Fork 本项目,再通过连接到 GitHub 仓库功能部署,而不是直接使用一键部署 URL。 ::: @@ -568,10 +251,7 @@ The admin password is set during the install wizard. To reset, you can set `ADMI ::: details 如何重置管理员密码? 管理员密码在安装向导中设置。如需重置,可临时设置 `ADMIN_PASS` 并重新部署,或清空已持久化的配置后重新运行向导。 ::: -::: - -## Repository { lang="en" } -## 项目仓库 { lang="zh-CN" } +## 项目仓库 - [OpenListTeam/OpenList-Worker](https://github.com/OpenListTeam/OpenList-Worker) diff --git a/pages/guide/migrate.md b/pages/guide/migrate.md index 543160536..cd5109412 100644 --- a/pages/guide/migrate.md +++ b/pages/guide/migrate.md @@ -1,62 +1,26 @@ --- -title: - en: Migrate from AList V3 - zh-CN: 从 AList V3 迁移 categories: - guide top: 99999 --- -::: en -::: danger -Smooth migration is not compatible with Alist v3.46 and higher versions. If you want to migrate, please avoid upgrading to a higher version. -::: -::: zh-CN +# 从 AList V3 迁移 + ::: danger 不兼容 Alist v3.46 及更高版本的平滑迁移,如需迁移请勿升级到更高版本。 ::: -## 1. Revoke Authorization for Alist API { lang="en" } - -## 1. 解除授权 Alist API { lang="zh-CN" } +## 1. 解除授权 Alist API -::: en -As the API service provided by the original author may have been taken over by a third party, there is a risk of information leakage or account bans. If you have concerns, it is recommended to revoke authorization or log in again to ensure your security. - -Since it's not possible to provide detailed steps for every platform, we invite helpful users to visit [https://github.com/OpenListTeam/OpenList-Docs](https://github.com/OpenListTeam/OpenList-Docs) and share screenshots of the authorization revocation process. - -The following content is provided by helpful online users in [OpenListTeam/OpenList-Docs#5](https://github.com/OpenListTeam/OpenList-Docs/issues/5) and [OpenListTeam/OpenList-Docs#6](https://github.com/OpenListTeam/OpenList-Docs/issues/6). We thank them for their contributions!🙏✨💖 -::: -::: zh-CN 由于原作者提供的 API 服务可能已被第三方控制,存在信息泄露或账号封禁的风险。如果您对此有所顾虑,建议解除授权或重新登录以确保安全。 由于无法对各平台一一截取详细流程,欢迎各位热心网友前往 https://github.com/OpenListTeam/OpenList-Docs 提供解除授权的流程截图。 以下内容由热心网友提供([OpenListTeam/OpenList-Docs#5](https://github.com/OpenListTeam/OpenList-Docs/issues/5)、[OpenListTeam/OpenList-Docs#6](https://github.com/OpenListTeam/OpenList-Docs/issues/6)),特别感谢!🙏✨💖 -::: -::: en -::: info The following methods only apply to cloud drives mounted using officially supported Open API methods. -::: - -::: zh-CN ::: info 以下方法仅适用于采用 Open API 官方支持的方式挂载的网盘。 ::: -::: en -::: tip Quick overview of how to revoke authorization for each cloud drive ♿️ - -- **Baidu Netdisk App** - My - Settings - Account Management - Authorization Management - Alist - Revoke Authorization -- **Aliyun Drive** - My - Upper right gear icon - Privacy Settings - Authorization Management - Alist - Revoke Authorization -- **115 APP** - Life (scroll down) - Account & Security - Multi-device Login Management - Third-party Login -- **China Unicom Cloud** - Check login account on webpage - Future recommendation: follow tutorial for packet capture login -- **Baidu Photo** - Avatar - App Settings - Account Management - Authorization Management -- **JianGuoYun** - Upper left three lines - Settings - Third-party App Management - Revoke Authorization -- **OneDrive** - https://account.live.com/consent/Manage - Revoke Authorization - -::: - -::: zh-CN ::: tip 速览各网盘解除授权方式 ♿️ - **百度网盘App** - 我的 - 设置 - 帐号管理 - 授权管理 - Alist - 解除授权 @@ -69,22 +33,7 @@ The following content is provided by helpful online users in [OpenListTeam/OpenL ::: -### Aliyun Drive { lang="en" } - -### 阿里云盘 { lang="zh-CN" } - -::: en - -1. Log in to Aliyun Drive -2. Visit the link: [https://www.alipan.com/o/oauth/auth-list](https://www.alipan.com/o/oauth/auth-list) - ![](/img/guide/migrate/aliyun_remove1.png) -3. Find Alist under “**Authorized Cloud Services**,” click to enter, then click "**Revoke Authorization**" - ![](/img/guide/migrate/aliyun_remove2.png) -4. Successfully revoked - ![](/img/guide/migrate/aliyun_remove3.png) - -::: -::: zh-CN +### 阿里云盘 1. 登陆阿里云盘 2. 访问链接 https://www.alipan.com/o/oauth/auth-list @@ -94,30 +43,11 @@ The following content is provided by helpful online users in [OpenListTeam/OpenL 4. 解除成功 ![](/img/guide/migrate/aliyun_remove3.png) -::: - -### Aliyun Drive APP { lang="en" } - -### 阿里云盘 APP { lang="zh-CN" } +### 阿里云盘 APP ![](/img/guide/migrate/aliyun_remove4.jpg) -### Baidu Netdisk { lang="en" } - -### 百度网盘 { lang="zh-CN" } - -::: en - -1. Log in to Baidu Netdisk -2. Visit the link: [https://passport.baidu.com/v6/appAuthority](https://passport.baidu.com/v6/appAuthority) - ![](/img/guide/migrate/baidu_remove1.png) -3. Find Alist in Authorization Management, click to enter, then click "**Revoke Authorization**" - ![](/img/guide/migrate/baidu_remove2.png) -4. Successfully revoked - -::: - -::: zh-CN +### 百度网盘 1. 登陆百度网盘 2. 访问链接 https://passport.baidu.com/v6/appAuthority @@ -126,25 +56,17 @@ The following content is provided by helpful online users in [OpenListTeam/OpenL ![](/img/guide/migrate/baidu_remove2.png) 4. 解除成功 -::: - -### Baidu Netdisk APP { lang="en" } - -### 百度网盘 APP { lang="zh-CN" } +### 百度网盘 APP ![](/img/guide/migrate/baidu_remove3.jpg) -### OneDrive Business { lang="en" } - -### OneDrive 商业版 { lang="zh-CN" } +### OneDrive 商业版 Link: https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade/quickStartType~/null/sourceType/Microsoft_AAD_IAM?Microsoft_AAD_IAM_legacyAADRedirect=true ![](/img/guide/migrate/odb_remove1.jpg) -### OneDrive Personal { lang="en" } - -### OneDrive 个人版 { lang="zh-CN" } +### OneDrive 个人版 Link: https://account.live.com/consent/Manage @@ -160,78 +82,32 @@ Link: https://console.cloud.google.com ![](/img/guide/migrate/google_remove3.png) -## 2. Backup Configuration Files { lang="en" } - -## 2. 备份配置文件 { lang="zh-CN" } +## 2. 备份配置文件 -::: en -Use the [Backup & Restore](/guide/advanced/backup) function to back up the configuration files to your local device. - -Additionally, you will need to back up the `data` folder from Alist V3, which contains site configuration files and the database. -::: -::: zh-CN 使用 [备份&恢复](/guide/advanced/backup) 功能,将配置文件进行备份到本地。 此外,您还需要备份 Alist V3 的 `data` 文件夹,里面包含着站点的配置文件以及数据库。 -::: - -## 3. Uninstall Alist V3 { lang="en" } -## 3. 卸载 Alist V3 { lang="zh-CN" } +## 3. 卸载 Alist V3 -::: en -Uninstall according to the method you used for installation. -::: -::: zh-CN 根据您安装的方式进行卸载。 -::: - -## 4. Install OpenList { lang="en" } -## 4. 安装 OpenList { lang="zh-CN" } +## 4. 安装 OpenList -::: en -Follow the instructions provided in the documentation to install OpenList. -::: -::: zh-CN 通过文档提供的方式安装 OpenList。 -::: -::: en -::: danger -If you are using Docker for deployment, make sure to modify the Volume mapping by changing the configuration file path from `/opt/alist/data` to `/opt/openlist/data`. Otherwise, your configuration files will be lost after updating the version and rebuilding the container! - -In addition, it is recommended to delete the previous container and then recreate it to avoid failure to run due to environmental changes. -::: -::: zh-CN ::: danger 如果您使用 Docker 部署,请确保修改 Volume 映射,将配置文件的路径从 `/opt/alist/data` 修改为 `/opt/openlist/data`。否则,更新版本、重建容器后您的配置文件将丢失! 此外,建议删除之前的容器,然后重新创建,避免环境变化导致无法运行。 ::: -## 5. Restore Configuration Files{ lang="en" } +## 5. 恢复配置文件 -## 5. 恢复配置文件 { lang="zh-CN" } - -::: en -If your Alist V3 version is below v3.46, you can migrate directly—keeping the previous `data` folder and only replacing the OpenList binary files. - -Otherwise, use the [Backup & Restore](/guide/advanced/backup) function to restore the backed-up configuration files to OpenList. -::: -::: zh-CN 如果您的 Alist V3 版本低于 v3.46,正常情况下,您可以直接迁移——即保留之前的 `data` 文件夹,仅替换 OpenList 的二进制文件。 否则,请使用 [备份&恢复](/guide/advanced/backup) 功能,将备份的配置文件恢复到 OpenList。 -::: -## 6. Reset Settings { lang="en" } +## 6. 重置设置 -## 6. 重置设置 { lang="zh-CN" } - -::: en -Once in OpenList’s admin panel, on each settings page, click the `Load Default Settings` button at the bottom, then click `Save`. -::: -::: zh-CN 进入 OpenList 后台,在设置的各个页面下,点击下方的 `加载默认设置`,然后 `保存`。 -::: diff --git a/pages/index.md b/pages/index.md index d206df874..8e97b9a6f 100644 --- a/pages/index.md +++ b/pages/index.md @@ -1,36 +1,30 @@ --- -style: home +layout: home title: OpenList -description: - en: OpenList is a resilient, community-driven file list program that supports multiple cloud storage platforms. Built as a secure fork of AList with enhanced security and long-term governance. - zh-CN: OpenList 是一个支持多种云存储平台的韧性文件列表程序。作为 AList 的安全分支,具有增强的安全性和长期治理保障。 -# titleTemplate: '%s - Next Generation Static Blog Framework' +titleTemplate: OpenList 文档 +description: OpenList 是一个支持多种云存储平台的韧性文件列表程序。作为 AList 的安全分支,具有增强的安全性和长期治理保障。 +comment: false hero: name: OpenList - text: + tagline: 🗂️ 一个支持多种存储的文件列表程序,使用 Gin 和 SolidJS + image: + src: /logo.svg + alt: OpenList actions: - theme: brand - text: hero.actions.get-started - link: /guide - type: fly + text: 快速上手 + link: /guide/ - theme: alt - text: hero.actions.view-github + text: 查看 GitHub link: https://github.com/OpenListTeam/OpenList - theme: alt - external: true - text: hero.actions.disclaimer + text: 免责声明 link: /terms#disclaimer - theme: alt - external: true - text: hero.actions.terms + text: 使用条款 link: /terms - theme: alt - external: true - text: hero.actions.privacy + text: 隐私政策 link: /privacy -# todo, rewrite with valaxy -# features: --- - - diff --git a/pages/privacy.md b/pages/privacy.md index 95148022c..c542fa5bc 100644 --- a/pages/privacy.md +++ b/pages/privacy.md @@ -1,27 +1,17 @@ --- -title: - en: Privacy Policy - zh-CN: 隐私政策 -# This is the icon of the page -nav: false -hide: true -toc: false -layout: statement -top: 1000 -# A page can have multiple categories +sidebar: false +aside: false +outline: false +pageClass: statement-page +comment: false +contributors: false --- -::: zh-CN -我们非常重视您的隐私。我们**不收集任何您的个人信息**。但请注意,您的IP地址等信息可能会被您的互联网服务提供商(ISP)或网络中转节点收集,这超出我们的控制范围。 -::: - -::: en -We highly value your privacy. We **DO NOT collect any of your personal information**. However, please be aware that your IP address and other metadata might be collected by your ISP or intermediate network nodes beyond our control. -::: +# 隐私政策 -## 隐私政策 { lang="zh-CN" } +我们非常重视您的隐私。我们**不收集任何您的个人信息**。但请注意,您的IP地址等信息可能会被您的互联网服务提供商(ISP)或网络中转节点收集,这超出我们的控制范围。 -::: zh-CN +## 隐私政策 - 我们不会收集、存储、处理或分析您的任何个人信息。 - 我们不会使用任何追踪技术(如Cookies、指纹识别、Web Beacon等)来收集您的信息。 @@ -29,18 +19,3 @@ We highly value your privacy. We **DO NOT collect any of your personal informati - 您在本服务中的所有操作均不会被我们记录。 - 但您的IP地址、访问时间等信息可能会被您的ISP或网络中转节点收集,这属于互联网基础设施的正常行为,我们无法控制。 - 如有任何隐私相关疑问,欢迎随时联系我们。 - -::: - -## Privacy Policy { lang="en" } - -::: en - -- We DO NOT collect, store, process, or analyze any of your personal data. -- We DO NOT use any tracking technologies (such as cookies, fingerprinting, web beacons, etc.) to gather your data. -- We DO NOT share your information with any third parties. -- Your actions within this service are NOT recorded by us. -- HOWEVER, your IP address and visit time may be collected by your ISP or transit nodes as part of normal Internet infrastructure behavior, which is BEYOND our control. -- If you have any privacy-related concerns, please feel free to contact us. - -::: diff --git a/public/favicon.svg b/pages/public/favicon.svg similarity index 100% rename from public/favicon.svg rename to pages/public/favicon.svg diff --git a/public/img/1panel/container.png b/pages/public/img/1panel/container.png similarity index 100% rename from public/img/1panel/container.png rename to pages/public/img/1panel/container.png diff --git a/public/img/1panel/install.png b/pages/public/img/1panel/install.png similarity index 100% rename from public/img/1panel/install.png rename to pages/public/img/1panel/install.png diff --git a/public/img/1panel/installed.png b/pages/public/img/1panel/installed.png similarity index 100% rename from public/img/1panel/installed.png rename to pages/public/img/1panel/installed.png diff --git a/public/img/1panel/search.png b/pages/public/img/1panel/search.png similarity index 100% rename from public/img/1panel/search.png rename to pages/public/img/1panel/search.png diff --git a/public/img/1panel/terminal.png b/pages/public/img/1panel/terminal.png similarity index 100% rename from public/img/1panel/terminal.png rename to pages/public/img/1panel/terminal.png diff --git a/public/img/advanced/TrollStore.jpg b/pages/public/img/advanced/TrollStore.jpg similarity index 100% rename from public/img/advanced/TrollStore.jpg rename to pages/public/img/advanced/TrollStore.jpg diff --git a/public/img/advanced/backup.png b/pages/public/img/advanced/backup.png similarity index 100% rename from public/img/advanced/backup.png rename to pages/public/img/advanced/backup.png diff --git a/public/img/advanced/balance.png b/pages/public/img/advanced/balance.png similarity index 100% rename from public/img/advanced/balance.png rename to pages/public/img/advanced/balance.png diff --git a/public/img/advanced/casdoor-user.png b/pages/public/img/advanced/casdoor-user.png similarity index 100% rename from public/img/advanced/casdoor-user.png rename to pages/public/img/advanced/casdoor-user.png diff --git a/public/img/advanced/casdoor.png b/pages/public/img/advanced/casdoor.png similarity index 100% rename from public/img/advanced/casdoor.png rename to pages/public/img/advanced/casdoor.png diff --git a/public/img/advanced/dingding.png b/pages/public/img/advanced/dingding.png similarity index 100% rename from public/img/advanced/dingding.png rename to pages/public/img/advanced/dingding.png diff --git a/public/img/advanced/github-login.png b/pages/public/img/advanced/github-login.png similarity index 100% rename from public/img/advanced/github-login.png rename to pages/public/img/advanced/github-login.png diff --git a/public/img/advanced/github.png b/pages/public/img/advanced/github.png similarity index 100% rename from public/img/advanced/github.png rename to pages/public/img/advanced/github.png diff --git a/public/img/advanced/google.png b/pages/public/img/advanced/google.png similarity index 100% rename from public/img/advanced/google.png rename to pages/public/img/advanced/google.png diff --git a/public/img/advanced/hide-tips.png b/pages/public/img/advanced/hide-tips.png similarity index 100% rename from public/img/advanced/hide-tips.png rename to pages/public/img/advanced/hide-tips.png diff --git a/public/img/advanced/home.png b/pages/public/img/advanced/home.png similarity index 100% rename from public/img/advanced/home.png rename to pages/public/img/advanced/home.png diff --git a/public/img/advanced/index_settings_panel.png b/pages/public/img/advanced/index_settings_panel.png similarity index 100% rename from public/img/advanced/index_settings_panel.png rename to pages/public/img/advanced/index_settings_panel.png diff --git a/public/img/advanced/meta/password_b.png b/pages/public/img/advanced/meta/password_b.png similarity index 100% rename from public/img/advanced/meta/password_b.png rename to pages/public/img/advanced/meta/password_b.png diff --git a/public/img/advanced/meta/password_h.png b/pages/public/img/advanced/meta/password_h.png similarity index 100% rename from public/img/advanced/meta/password_h.png rename to pages/public/img/advanced/meta/password_h.png diff --git a/public/img/advanced/offline-download.png b/pages/public/img/advanced/offline-download.png similarity index 100% rename from public/img/advanced/offline-download.png rename to pages/public/img/advanced/offline-download.png diff --git a/public/img/advanced/qbittorrent.png b/pages/public/img/advanced/qbittorrent.png similarity index 100% rename from public/img/advanced/qbittorrent.png rename to pages/public/img/advanced/qbittorrent.png diff --git a/public/img/advanced/r_upload_1.png b/pages/public/img/advanced/r_upload_1.png similarity index 100% rename from public/img/advanced/r_upload_1.png rename to pages/public/img/advanced/r_upload_1.png diff --git a/public/img/advanced/r_upload_2.png b/pages/public/img/advanced/r_upload_2.png similarity index 100% rename from public/img/advanced/r_upload_2.png rename to pages/public/img/advanced/r_upload_2.png diff --git a/public/img/advanced/s3/s3_add_backup.png b/pages/public/img/advanced/s3/s3_add_backup.png similarity index 100% rename from public/img/advanced/s3/s3_add_backup.png rename to pages/public/img/advanced/s3/s3_add_backup.png diff --git a/public/img/advanced/s3/s3_config.png b/pages/public/img/advanced/s3/s3_config.png similarity index 100% rename from public/img/advanced/s3/s3_config.png rename to pages/public/img/advanced/s3/s3_config.png diff --git a/public/img/advanced/s3/s3browser.png b/pages/public/img/advanced/s3/s3browser.png similarity index 100% rename from public/img/advanced/s3/s3browser.png rename to pages/public/img/advanced/s3/s3browser.png diff --git a/public/img/advanced/s3/s3drive.png b/pages/public/img/advanced/s3/s3drive.png similarity index 100% rename from public/img/advanced/s3/s3drive.png rename to pages/public/img/advanced/s3/s3drive.png diff --git a/public/img/advanced/sqlite-mysql.png b/pages/public/img/advanced/sqlite-mysql.png similarity index 100% rename from public/img/advanced/sqlite-mysql.png rename to pages/public/img/advanced/sqlite-mysql.png diff --git a/public/img/advanced/sqlite3.png b/pages/public/img/advanced/sqlite3.png similarity index 100% rename from public/img/advanced/sqlite3.png rename to pages/public/img/advanced/sqlite3.png diff --git a/public/img/advanced/sso-add.png b/pages/public/img/advanced/sso-add.png similarity index 100% rename from public/img/advanced/sso-add.png rename to pages/public/img/advanced/sso-add.png diff --git a/public/img/advanced/sso-dir.png b/pages/public/img/advanced/sso-dir.png similarity index 100% rename from public/img/advanced/sso-dir.png rename to pages/public/img/advanced/sso-dir.png diff --git a/public/img/advanced/sso-group.png b/pages/public/img/advanced/sso-group.png similarity index 100% rename from public/img/advanced/sso-group.png rename to pages/public/img/advanced/sso-group.png diff --git a/public/img/advanced/sso-org-config.png b/pages/public/img/advanced/sso-org-config.png similarity index 100% rename from public/img/advanced/sso-org-config.png rename to pages/public/img/advanced/sso-org-config.png diff --git a/public/img/advanced/sso-permission_b.png b/pages/public/img/advanced/sso-permission_b.png similarity index 100% rename from public/img/advanced/sso-permission_b.png rename to pages/public/img/advanced/sso-permission_b.png diff --git a/public/img/advanced/sso-permission_h.png b/pages/public/img/advanced/sso-permission_h.png similarity index 100% rename from public/img/advanced/sso-permission_h.png rename to pages/public/img/advanced/sso-permission_h.png diff --git a/public/img/advanced/sso/keycloak-01.png b/pages/public/img/advanced/sso/keycloak-01.png similarity index 100% rename from public/img/advanced/sso/keycloak-01.png rename to pages/public/img/advanced/sso/keycloak-01.png diff --git a/public/img/advanced/sso/keycloak-02.png b/pages/public/img/advanced/sso/keycloak-02.png similarity index 100% rename from public/img/advanced/sso/keycloak-02.png rename to pages/public/img/advanced/sso/keycloak-02.png diff --git a/public/img/advanced/sso/keycloak-03.png b/pages/public/img/advanced/sso/keycloak-03.png similarity index 100% rename from public/img/advanced/sso/keycloak-03.png rename to pages/public/img/advanced/sso/keycloak-03.png diff --git a/public/img/advanced/sso_add_app.png b/pages/public/img/advanced/sso_add_app.png similarity index 100% rename from public/img/advanced/sso_add_app.png rename to pages/public/img/advanced/sso_add_app.png diff --git a/public/img/advanced/sso_cm.png b/pages/public/img/advanced/sso_cm.png similarity index 100% rename from public/img/advanced/sso_cm.png rename to pages/public/img/advanced/sso_cm.png diff --git a/public/img/advanced/sso_fix_logo.png b/pages/public/img/advanced/sso_fix_logo.png similarity index 100% rename from public/img/advanced/sso_fix_logo.png rename to pages/public/img/advanced/sso_fix_logo.png diff --git a/public/img/advanced/user_manage.png b/pages/public/img/advanced/user_manage.png similarity index 100% rename from public/img/advanced/user_manage.png rename to pages/public/img/advanced/user_manage.png diff --git a/public/img/advanced/user_read_archives_dark.png b/pages/public/img/advanced/user_read_archives_dark.png similarity index 100% rename from public/img/advanced/user_read_archives_dark.png rename to pages/public/img/advanced/user_read_archives_dark.png diff --git a/public/img/advanced/user_read_archives_light.png b/pages/public/img/advanced/user_read_archives_light.png similarity index 100% rename from public/img/advanced/user_read_archives_light.png rename to pages/public/img/advanced/user_read_archives_light.png diff --git a/public/img/advanced/video.png b/pages/public/img/advanced/video.png similarity index 100% rename from public/img/advanced/video.png rename to pages/public/img/advanced/video.png diff --git a/public/img/advanced/weiruan.png b/pages/public/img/advanced/weiruan.png similarity index 100% rename from public/img/advanced/weiruan.png rename to pages/public/img/advanced/weiruan.png diff --git a/public/img/api/apifox-dark.png b/pages/public/img/api/apifox-dark.png similarity index 100% rename from public/img/api/apifox-dark.png rename to pages/public/img/api/apifox-dark.png diff --git a/public/img/api/apifox-light.png b/pages/public/img/api/apifox-light.png similarity index 100% rename from public/img/api/apifox-light.png rename to pages/public/img/api/apifox-light.png diff --git a/public/img/config/open-with.png b/pages/public/img/config/open-with.png similarity index 100% rename from public/img/config/open-with.png rename to pages/public/img/config/open-with.png diff --git a/public/img/config/readme_b.png b/pages/public/img/config/readme_b.png similarity index 100% rename from public/img/config/readme_b.png rename to pages/public/img/config/readme_b.png diff --git a/public/img/config/readme_h.png b/pages/public/img/config/readme_h.png similarity index 100% rename from public/img/config/readme_h.png rename to pages/public/img/config/readme_h.png diff --git a/public/img/config/side/offline-download-modal-en.png b/pages/public/img/config/side/offline-download-modal-en.png similarity index 100% rename from public/img/config/side/offline-download-modal-en.png rename to pages/public/img/config/side/offline-download-modal-en.png diff --git a/public/img/config/side/offline-download-modal-zh.png b/pages/public/img/config/side/offline-download-modal-zh.png similarity index 100% rename from public/img/config/side/offline-download-modal-zh.png rename to pages/public/img/config/side/offline-download-modal-zh.png diff --git a/public/img/config/side/upload-modal-en.png b/pages/public/img/config/side/upload-modal-en.png similarity index 100% rename from public/img/config/side/upload-modal-en.png rename to pages/public/img/config/side/upload-modal-en.png diff --git a/public/img/config/side/upload-modal-zh.png b/pages/public/img/config/side/upload-modal-zh.png similarity index 100% rename from public/img/config/side/upload-modal-zh.png rename to pages/public/img/config/side/upload-modal-zh.png diff --git a/public/img/desktop/dashboard.png b/pages/public/img/desktop/dashboard.png similarity index 100% rename from public/img/desktop/dashboard.png rename to pages/public/img/desktop/dashboard.png diff --git a/public/img/desktop/logs.png b/pages/public/img/desktop/logs.png similarity index 100% rename from public/img/desktop/logs.png rename to pages/public/img/desktop/logs.png diff --git a/public/img/desktop/mount.png b/pages/public/img/desktop/mount.png similarity index 100% rename from public/img/desktop/mount.png rename to pages/public/img/desktop/mount.png diff --git a/public/img/drivers/115/115-add_error.png b/pages/public/img/drivers/115/115-add_error.png similarity index 100% rename from public/img/drivers/115/115-add_error.png rename to pages/public/img/drivers/115/115-add_error.png diff --git a/public/img/drivers/115/115-file_cid.png b/pages/public/img/drivers/115/115-file_cid.png similarity index 100% rename from public/img/drivers/115/115-file_cid.png rename to pages/public/img/drivers/115/115-file_cid.png diff --git a/public/img/drivers/115/115-open-01-d.png b/pages/public/img/drivers/115/115-open-01-d.png similarity index 100% rename from public/img/drivers/115/115-open-01-d.png rename to pages/public/img/drivers/115/115-open-01-d.png diff --git a/public/img/drivers/115/115-open-01-l.png b/pages/public/img/drivers/115/115-open-01-l.png similarity index 100% rename from public/img/drivers/115/115-open-01-l.png rename to pages/public/img/drivers/115/115-open-01-l.png diff --git a/public/img/drivers/115/115-open-02-01-d.png b/pages/public/img/drivers/115/115-open-02-01-d.png similarity index 100% rename from public/img/drivers/115/115-open-02-01-d.png rename to pages/public/img/drivers/115/115-open-02-01-d.png diff --git a/public/img/drivers/115/115-open-02-01-l.png b/pages/public/img/drivers/115/115-open-02-01-l.png similarity index 100% rename from public/img/drivers/115/115-open-02-01-l.png rename to pages/public/img/drivers/115/115-open-02-01-l.png diff --git a/public/img/drivers/115/115-open-02-02-d.png b/pages/public/img/drivers/115/115-open-02-02-d.png similarity index 100% rename from public/img/drivers/115/115-open-02-02-d.png rename to pages/public/img/drivers/115/115-open-02-02-d.png diff --git a/public/img/drivers/115/115-open-02-02-l.png b/pages/public/img/drivers/115/115-open-02-02-l.png similarity index 100% rename from public/img/drivers/115/115-open-02-02-l.png rename to pages/public/img/drivers/115/115-open-02-02-l.png diff --git a/public/img/drivers/115/115-open-03.png b/pages/public/img/drivers/115/115-open-03.png similarity index 100% rename from public/img/drivers/115/115-open-03.png rename to pages/public/img/drivers/115/115-open-03.png diff --git a/public/img/drivers/115/115-open-04-d.png b/pages/public/img/drivers/115/115-open-04-d.png similarity index 100% rename from public/img/drivers/115/115-open-04-d.png rename to pages/public/img/drivers/115/115-open-04-d.png diff --git a/public/img/drivers/115/115-open-04-l.png b/pages/public/img/drivers/115/115-open-04-l.png similarity index 100% rename from public/img/drivers/115/115-open-04-l.png rename to pages/public/img/drivers/115/115-open-04-l.png diff --git a/public/img/drivers/115/115-open-05-d.png b/pages/public/img/drivers/115/115-open-05-d.png similarity index 100% rename from public/img/drivers/115/115-open-05-d.png rename to pages/public/img/drivers/115/115-open-05-d.png diff --git a/public/img/drivers/115/115-open-05-l.png b/pages/public/img/drivers/115/115-open-05-l.png similarity index 100% rename from public/img/drivers/115/115-open-05-l.png rename to pages/public/img/drivers/115/115-open-05-l.png diff --git a/public/img/drivers/115/115-open-application.jpg b/pages/public/img/drivers/115/115-open-application.jpg similarity index 100% rename from public/img/drivers/115/115-open-application.jpg rename to pages/public/img/drivers/115/115-open-application.jpg diff --git a/public/img/drivers/115/115-root_cid.png b/pages/public/img/drivers/115/115-root_cid.png similarity index 100% rename from public/img/drivers/115/115-root_cid.png rename to pages/public/img/drivers/115/115-root_cid.png diff --git a/public/img/drivers/115/115-url_demo.png b/pages/public/img/drivers/115/115-url_demo.png similarity index 100% rename from public/img/drivers/115/115-url_demo.png rename to pages/public/img/drivers/115/115-url_demo.png diff --git a/public/img/drivers/123/123.png b/pages/public/img/drivers/123/123.png similarity index 100% rename from public/img/drivers/123/123.png rename to pages/public/img/drivers/123/123.png diff --git a/public/img/drivers/123/123_add_demo.png b/pages/public/img/drivers/123/123_add_demo.png similarity index 100% rename from public/img/drivers/123/123_add_demo.png rename to pages/public/img/drivers/123/123_add_demo.png diff --git a/public/img/drivers/123/123_fl_id.png b/pages/public/img/drivers/123/123_fl_id.png similarity index 100% rename from public/img/drivers/123/123_fl_id.png rename to pages/public/img/drivers/123/123_fl_id.png diff --git a/public/img/drivers/123/123_link_1.png b/pages/public/img/drivers/123/123_link_1.png similarity index 100% rename from public/img/drivers/123/123_link_1.png rename to pages/public/img/drivers/123/123_link_1.png diff --git a/public/img/drivers/123/123_link_2.png b/pages/public/img/drivers/123/123_link_2.png similarity index 100% rename from public/img/drivers/123/123_link_2.png rename to pages/public/img/drivers/123/123_link_2.png diff --git a/public/img/drivers/123/123_link_3.png b/pages/public/img/drivers/123/123_link_3.png similarity index 100% rename from public/img/drivers/123/123_link_3.png rename to pages/public/img/drivers/123/123_link_3.png diff --git a/public/img/drivers/123/123_link_4.png b/pages/public/img/drivers/123/123_link_4.png similarity index 100% rename from public/img/drivers/123/123_link_4.png rename to pages/public/img/drivers/123/123_link_4.png diff --git a/public/img/drivers/123/123open-02.png b/pages/public/img/drivers/123/123open-02.png similarity index 100% rename from public/img/drivers/123/123open-02.png rename to pages/public/img/drivers/123/123open-02.png diff --git a/public/img/drivers/123/123open-03.png b/pages/public/img/drivers/123/123open-03.png similarity index 100% rename from public/img/drivers/123/123open-03.png rename to pages/public/img/drivers/123/123open-03.png diff --git a/public/img/drivers/139/139_new_au.png b/pages/public/img/drivers/139/139_new_au.png similarity index 100% rename from public/img/drivers/139/139_new_au.png rename to pages/public/img/drivers/139/139_new_au.png diff --git a/public/img/drivers/139/add-family.png b/pages/public/img/drivers/139/add-family.png similarity index 100% rename from public/img/drivers/139/add-family.png rename to pages/public/img/drivers/139/add-family.png diff --git a/public/img/drivers/139/add-personal.png b/pages/public/img/drivers/139/add-personal.png similarity index 100% rename from public/img/drivers/139/add-personal.png rename to pages/public/img/drivers/139/add-personal.png diff --git a/public/img/drivers/139/add_new_personal.png b/pages/public/img/drivers/139/add_new_personal.png similarity index 100% rename from public/img/drivers/139/add_new_personal.png rename to pages/public/img/drivers/139/add_new_personal.png diff --git a/public/img/drivers/139/ch-family.png b/pages/public/img/drivers/139/ch-family.png similarity index 100% rename from public/img/drivers/139/ch-family.png rename to pages/public/img/drivers/139/ch-family.png diff --git a/public/img/drivers/139/ch-personal.png b/pages/public/img/drivers/139/ch-personal.png similarity index 100% rename from public/img/drivers/139/ch-personal.png rename to pages/public/img/drivers/139/ch-personal.png diff --git a/public/img/drivers/139/new_personal.png b/pages/public/img/drivers/139/new_personal.png similarity index 100% rename from public/img/drivers/139/new_personal.png rename to pages/public/img/drivers/139/new_personal.png diff --git a/public/img/drivers/139/other-family.png b/pages/public/img/drivers/139/other-family.png similarity index 100% rename from public/img/drivers/139/other-family.png rename to pages/public/img/drivers/139/other-family.png diff --git a/public/img/drivers/139/other-personal.png b/pages/public/img/drivers/139/other-personal.png similarity index 100% rename from public/img/drivers/139/other-personal.png rename to pages/public/img/drivers/139/other-personal.png diff --git a/public/img/drivers/163/163_cookie.png b/pages/public/img/drivers/163/163_cookie.png similarity index 100% rename from public/img/drivers/163/163_cookie.png rename to pages/public/img/drivers/163/163_cookie.png diff --git a/public/img/drivers/189/189-0.png b/pages/public/img/drivers/189/189-0.png similarity index 100% rename from public/img/drivers/189/189-0.png rename to pages/public/img/drivers/189/189-0.png diff --git a/public/img/drivers/189/189-1.png b/pages/public/img/drivers/189/189-1.png similarity index 100% rename from public/img/drivers/189/189-1.png rename to pages/public/img/drivers/189/189-1.png diff --git a/public/img/drivers/189/189-2.png b/pages/public/img/drivers/189/189-2.png similarity index 100% rename from public/img/drivers/189/189-2.png rename to pages/public/img/drivers/189/189-2.png diff --git a/public/img/drivers/189/189.png b/pages/public/img/drivers/189/189.png similarity index 100% rename from public/img/drivers/189/189.png rename to pages/public/img/drivers/189/189.png diff --git a/public/img/drivers/189/3.png b/pages/public/img/drivers/189/3.png similarity index 100% rename from public/img/drivers/189/3.png rename to pages/public/img/drivers/189/3.png diff --git a/public/img/drivers/189/4.png b/pages/public/img/drivers/189/4.png similarity index 100% rename from public/img/drivers/189/4.png rename to pages/public/img/drivers/189/4.png diff --git a/public/img/drivers/OneDrive/onedrive-new-password.png b/pages/public/img/drivers/OneDrive/onedrive-new-password.png similarity index 100% rename from public/img/drivers/OneDrive/onedrive-new-password.png rename to pages/public/img/drivers/OneDrive/onedrive-new-password.png diff --git a/public/img/drivers/OneDrive/onedrive-register-app.png b/pages/public/img/drivers/OneDrive/onedrive-register-app.png similarity index 100% rename from public/img/drivers/OneDrive/onedrive-register-app.png rename to pages/public/img/drivers/OneDrive/onedrive-register-app.png diff --git a/public/img/drivers/OneDrive/onedrive-update-permission.png b/pages/public/img/drivers/OneDrive/onedrive-update-permission.png similarity index 100% rename from public/img/drivers/OneDrive/onedrive-update-permission.png rename to pages/public/img/drivers/OneDrive/onedrive-update-permission.png diff --git a/public/img/drivers/OneDrive/online_api.png b/pages/public/img/drivers/OneDrive/online_api.png similarity index 100% rename from public/img/drivers/OneDrive/online_api.png rename to pages/public/img/drivers/OneDrive/online_api.png diff --git a/public/img/drivers/OneDrive/online_api_config.png b/pages/public/img/drivers/OneDrive/online_api_config.png similarity index 100% rename from public/img/drivers/OneDrive/online_api_config.png rename to pages/public/img/drivers/OneDrive/online_api_config.png diff --git a/public/img/drivers/alias/alias-2.png b/pages/public/img/drivers/alias/alias-2.png similarity index 100% rename from public/img/drivers/alias/alias-2.png rename to pages/public/img/drivers/alias/alias-2.png diff --git a/public/img/drivers/alias/alias-3.png b/pages/public/img/drivers/alias/alias-3.png similarity index 100% rename from public/img/drivers/alias/alias-3.png rename to pages/public/img/drivers/alias/alias-3.png diff --git a/public/img/drivers/alias/alias.png b/pages/public/img/drivers/alias/alias.png similarity index 100% rename from public/img/drivers/alias/alias.png rename to pages/public/img/drivers/alias/alias.png diff --git a/public/img/drivers/alias/read_policy_all.png b/pages/public/img/drivers/alias/read_policy_all.png similarity index 100% rename from public/img/drivers/alias/read_policy_all.png rename to pages/public/img/drivers/alias/read_policy_all.png diff --git a/public/img/drivers/alist/alist.png b/pages/public/img/drivers/alist/alist.png similarity index 100% rename from public/img/drivers/alist/alist.png rename to pages/public/img/drivers/alist/alist.png diff --git a/public/img/drivers/aliyun/aliyun-open-01-d.png b/pages/public/img/drivers/aliyun/aliyun-open-01-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-01-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-01-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-01-l.png b/pages/public/img/drivers/aliyun/aliyun-open-01-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-01-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-01-l.png diff --git a/public/img/drivers/aliyun/aliyun-open-02-01-d.png b/pages/public/img/drivers/aliyun/aliyun-open-02-01-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-02-01-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-02-01-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-02-01-l.png b/pages/public/img/drivers/aliyun/aliyun-open-02-01-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-02-01-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-02-01-l.png diff --git a/public/img/drivers/aliyun/aliyun-open-02-02-d.png b/pages/public/img/drivers/aliyun/aliyun-open-02-02-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-02-02-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-02-02-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-02-02-l.png b/pages/public/img/drivers/aliyun/aliyun-open-02-02-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-02-02-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-02-02-l.png diff --git a/public/img/drivers/aliyun/aliyun-open-03.png b/pages/public/img/drivers/aliyun/aliyun-open-03.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-03.png rename to pages/public/img/drivers/aliyun/aliyun-open-03.png diff --git a/public/img/drivers/aliyun/aliyun-open-04-d.png b/pages/public/img/drivers/aliyun/aliyun-open-04-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-04-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-04-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-04-l.png b/pages/public/img/drivers/aliyun/aliyun-open-04-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-04-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-04-l.png diff --git a/public/img/drivers/aliyun/aliyun-open-05-d.png b/pages/public/img/drivers/aliyun/aliyun-open-05-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-05-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-05-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-05-l.png b/pages/public/img/drivers/aliyun/aliyun-open-05-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-05-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-05-l.png diff --git a/public/img/drivers/aliyun/aliyun-open-06-d.png b/pages/public/img/drivers/aliyun/aliyun-open-06-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-06-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-06-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-06-l.png b/pages/public/img/drivers/aliyun/aliyun-open-06-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-06-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-06-l.png diff --git a/public/img/drivers/aliyun/aliyun-open-07-d.png b/pages/public/img/drivers/aliyun/aliyun-open-07-d.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-07-d.png rename to pages/public/img/drivers/aliyun/aliyun-open-07-d.png diff --git a/public/img/drivers/aliyun/aliyun-open-07-l.png b/pages/public/img/drivers/aliyun/aliyun-open-07-l.png similarity index 100% rename from public/img/drivers/aliyun/aliyun-open-07-l.png rename to pages/public/img/drivers/aliyun/aliyun-open-07-l.png diff --git a/public/img/drivers/aliyun/aliyun_api.png b/pages/public/img/drivers/aliyun/aliyun_api.png similarity index 100% rename from public/img/drivers/aliyun/aliyun_api.png rename to pages/public/img/drivers/aliyun/aliyun_api.png diff --git a/public/img/drivers/aliyun/aliyun_type.png b/pages/public/img/drivers/aliyun/aliyun_type.png similarity index 100% rename from public/img/drivers/aliyun/aliyun_type.png rename to pages/public/img/drivers/aliyun/aliyun_type.png diff --git a/public/img/drivers/aliyundrive.png b/pages/public/img/drivers/aliyundrive.png similarity index 100% rename from public/img/drivers/aliyundrive.png rename to pages/public/img/drivers/aliyundrive.png diff --git a/public/img/drivers/baidu/BDUSS.png b/pages/public/img/drivers/baidu/BDUSS.png similarity index 100% rename from public/img/drivers/baidu/BDUSS.png rename to pages/public/img/drivers/baidu/BDUSS.png diff --git a/public/img/drivers/baidu/aria2-ua.png b/pages/public/img/drivers/baidu/aria2-ua.png similarity index 100% rename from public/img/drivers/baidu/aria2-ua.png rename to pages/public/img/drivers/baidu/aria2-ua.png diff --git a/public/img/drivers/baidu/bdUA.png b/pages/public/img/drivers/baidu/bdUA.png similarity index 100% rename from public/img/drivers/baidu/bdUA.png rename to pages/public/img/drivers/baidu/bdUA.png diff --git a/public/img/drivers/baidu/crack_dev_token.png b/pages/public/img/drivers/baidu/crack_dev_token.png similarity index 100% rename from public/img/drivers/baidu/crack_dev_token.png rename to pages/public/img/drivers/baidu/crack_dev_token.png diff --git a/public/img/drivers/baidu/dev_app_console.png b/pages/public/img/drivers/baidu/dev_app_console.png similarity index 100% rename from public/img/drivers/baidu/dev_app_console.png rename to pages/public/img/drivers/baidu/dev_app_console.png diff --git a/public/img/drivers/baidu/dev_app_create_button.png b/pages/public/img/drivers/baidu/dev_app_create_button.png similarity index 100% rename from public/img/drivers/baidu/dev_app_create_button.png rename to pages/public/img/drivers/baidu/dev_app_create_button.png diff --git a/public/img/drivers/baidu/dev_app_create_panel.png b/pages/public/img/drivers/baidu/dev_app_create_panel.png similarity index 100% rename from public/img/drivers/baidu/dev_app_create_panel.png rename to pages/public/img/drivers/baidu/dev_app_create_panel.png diff --git a/public/img/drivers/baidu/dev_app_info.png b/pages/public/img/drivers/baidu/dev_app_info.png similarity index 100% rename from public/img/drivers/baidu/dev_app_info.png rename to pages/public/img/drivers/baidu/dev_app_info.png diff --git a/public/img/drivers/baidu/dev_app_settings_safety.png b/pages/public/img/drivers/baidu/dev_app_settings_safety.png similarity index 100% rename from public/img/drivers/baidu/dev_app_settings_safety.png rename to pages/public/img/drivers/baidu/dev_app_settings_safety.png diff --git a/public/img/drivers/baidu/dev_platform.png b/pages/public/img/drivers/baidu/dev_platform.png similarity index 100% rename from public/img/drivers/baidu/dev_platform.png rename to pages/public/img/drivers/baidu/dev_platform.png diff --git a/public/img/drivers/baidu/dev_platform_console.png b/pages/public/img/drivers/baidu/dev_platform_console.png similarity index 100% rename from public/img/drivers/baidu/dev_platform_console.png rename to pages/public/img/drivers/baidu/dev_platform_console.png diff --git a/public/img/drivers/baidu/dev_platform_login.png b/pages/public/img/drivers/baidu/dev_platform_login.png similarity index 100% rename from public/img/drivers/baidu/dev_platform_login.png rename to pages/public/img/drivers/baidu/dev_platform_login.png diff --git a/public/img/drivers/baidu/dev_token.png b/pages/public/img/drivers/baidu/dev_token.png similarity index 100% rename from public/img/drivers/baidu/dev_token.png rename to pages/public/img/drivers/baidu/dev_token.png diff --git a/public/img/drivers/baidu/idm-ua.png b/pages/public/img/drivers/baidu/idm-ua.png similarity index 100% rename from public/img/drivers/baidu/idm-ua.png rename to pages/public/img/drivers/baidu/idm-ua.png diff --git a/public/img/drivers/baidu/motrix-ua.png b/pages/public/img/drivers/baidu/motrix-ua.png similarity index 100% rename from public/img/drivers/baidu/motrix-ua.png rename to pages/public/img/drivers/baidu/motrix-ua.png diff --git a/public/img/drivers/baidu/non_dev_token.png b/pages/public/img/drivers/baidu/non_dev_token.png similarity index 100% rename from public/img/drivers/baidu/non_dev_token.png rename to pages/public/img/drivers/baidu/non_dev_token.png diff --git a/public/img/drivers/baidu/openapi.png b/pages/public/img/drivers/baidu/openapi.png similarity index 100% rename from public/img/drivers/baidu/openapi.png rename to pages/public/img/drivers/baidu/openapi.png diff --git a/public/img/drivers/baidu/yike_cookie.png b/pages/public/img/drivers/baidu/yike_cookie.png similarity index 100% rename from public/img/drivers/baidu/yike_cookie.png rename to pages/public/img/drivers/baidu/yike_cookie.png diff --git a/public/img/drivers/chaoxing/chaoxing_bbsid.png b/pages/public/img/drivers/chaoxing/chaoxing_bbsid.png similarity index 100% rename from public/img/drivers/chaoxing/chaoxing_bbsid.png rename to pages/public/img/drivers/chaoxing/chaoxing_bbsid.png diff --git a/public/img/drivers/chaoxing/chaoxing_folder_id.gif b/pages/public/img/drivers/chaoxing/chaoxing_folder_id.gif similarity index 100% rename from public/img/drivers/chaoxing/chaoxing_folder_id.gif rename to pages/public/img/drivers/chaoxing/chaoxing_folder_id.gif diff --git a/public/img/drivers/chunk/chunk_large_only_disabled.png b/pages/public/img/drivers/chunk/chunk_large_only_disabled.png similarity index 100% rename from public/img/drivers/chunk/chunk_large_only_disabled.png rename to pages/public/img/drivers/chunk/chunk_large_only_disabled.png diff --git a/public/img/drivers/chunk/chunk_large_only_enabled.png b/pages/public/img/drivers/chunk/chunk_large_only_enabled.png similarity index 100% rename from public/img/drivers/chunk/chunk_large_only_enabled.png rename to pages/public/img/drivers/chunk/chunk_large_only_enabled.png diff --git a/public/img/drivers/chunk/custom_ext_disabled.png b/pages/public/img/drivers/chunk/custom_ext_disabled.png similarity index 100% rename from public/img/drivers/chunk/custom_ext_disabled.png rename to pages/public/img/drivers/chunk/custom_ext_disabled.png diff --git a/public/img/drivers/chunk/custom_ext_enabled.png b/pages/public/img/drivers/chunk/custom_ext_enabled.png similarity index 100% rename from public/img/drivers/chunk/custom_ext_enabled.png rename to pages/public/img/drivers/chunk/custom_ext_enabled.png diff --git a/public/img/drivers/chunk/example.png b/pages/public/img/drivers/chunk/example.png similarity index 100% rename from public/img/drivers/chunk/example.png rename to pages/public/img/drivers/chunk/example.png diff --git a/public/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png b/pages/public/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png similarity index 100% rename from public/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png rename to pages/public/img/drivers/cloudflare-imgbed/cloudflare-imgbed-1.png diff --git a/public/img/drivers/cloudreve/cloudreve_get_cookie.png b/pages/public/img/drivers/cloudreve/cloudreve_get_cookie.png similarity index 100% rename from public/img/drivers/cloudreve/cloudreve_get_cookie.png rename to pages/public/img/drivers/cloudreve/cloudreve_get_cookie.png diff --git a/public/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png b/pages/public/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png similarity index 100% rename from public/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png rename to pages/public/img/drivers/cloudreve_v4/cloudreve_v4_token_local_storage.png diff --git a/public/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png b/pages/public/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png similarity index 100% rename from public/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png rename to pages/public/img/drivers/cloudreve_v4/cloudreve_v4_token_resp.png diff --git a/public/img/drivers/common/path_b.png b/pages/public/img/drivers/common/path_b.png similarity index 100% rename from public/img/drivers/common/path_b.png rename to pages/public/img/drivers/common/path_b.png diff --git a/public/img/drivers/common/path_h.png b/pages/public/img/drivers/common/path_h.png similarity index 100% rename from public/img/drivers/common/path_h.png rename to pages/public/img/drivers/common/path_h.png diff --git a/public/img/drivers/crypt/crypt-demo1.png b/pages/public/img/drivers/crypt/crypt-demo1.png similarity index 100% rename from public/img/drivers/crypt/crypt-demo1.png rename to pages/public/img/drivers/crypt/crypt-demo1.png diff --git a/public/img/drivers/crypt/crypt-demo2.png b/pages/public/img/drivers/crypt/crypt-demo2.png similarity index 100% rename from public/img/drivers/crypt/crypt-demo2.png rename to pages/public/img/drivers/crypt/crypt-demo2.png diff --git a/public/img/drivers/doubao/doubao-cookie.png b/pages/public/img/drivers/doubao/doubao-cookie.png similarity index 100% rename from public/img/drivers/doubao/doubao-cookie.png rename to pages/public/img/drivers/doubao/doubao-cookie.png diff --git a/public/img/drivers/doubao/doubao-root-folder-id.png b/pages/public/img/drivers/doubao/doubao-root-folder-id.png similarity index 100% rename from public/img/drivers/doubao/doubao-root-folder-id.png rename to pages/public/img/drivers/doubao/doubao-root-folder-id.png diff --git a/public/img/drivers/dropbox/1.png b/pages/public/img/drivers/dropbox/1.png similarity index 100% rename from public/img/drivers/dropbox/1.png rename to pages/public/img/drivers/dropbox/1.png diff --git a/public/img/drivers/dropbox/2.png b/pages/public/img/drivers/dropbox/2.png similarity index 100% rename from public/img/drivers/dropbox/2.png rename to pages/public/img/drivers/dropbox/2.png diff --git a/public/img/drivers/dropbox/3.png b/pages/public/img/drivers/dropbox/3.png similarity index 100% rename from public/img/drivers/dropbox/3.png rename to pages/public/img/drivers/dropbox/3.png diff --git a/public/img/drivers/dropbox/4.png b/pages/public/img/drivers/dropbox/4.png similarity index 100% rename from public/img/drivers/dropbox/4.png rename to pages/public/img/drivers/dropbox/4.png diff --git a/public/img/drivers/dropbox/5.png b/pages/public/img/drivers/dropbox/5.png similarity index 100% rename from public/img/drivers/dropbox/5.png rename to pages/public/img/drivers/dropbox/5.png diff --git a/public/img/drivers/dropbox/6.png b/pages/public/img/drivers/dropbox/6.png similarity index 100% rename from public/img/drivers/dropbox/6.png rename to pages/public/img/drivers/dropbox/6.png diff --git a/public/img/drivers/dropbox/7.png b/pages/public/img/drivers/dropbox/7.png similarity index 100% rename from public/img/drivers/dropbox/7.png rename to pages/public/img/drivers/dropbox/7.png diff --git a/public/img/drivers/dropbox/folder_id.png b/pages/public/img/drivers/dropbox/folder_id.png similarity index 100% rename from public/img/drivers/dropbox/folder_id.png rename to pages/public/img/drivers/dropbox/folder_id.png diff --git a/public/img/drivers/febbox/febox.png b/pages/public/img/drivers/febbox/febox.png similarity index 100% rename from public/img/drivers/febbox/febox.png rename to pages/public/img/drivers/febbox/febox.png diff --git a/public/img/drivers/feiji/feiji.png b/pages/public/img/drivers/feiji/feiji.png similarity index 100% rename from public/img/drivers/feiji/feiji.png rename to pages/public/img/drivers/feiji/feiji.png diff --git a/public/img/drivers/github/1.png b/pages/public/img/drivers/github/1.png similarity index 100% rename from public/img/drivers/github/1.png rename to pages/public/img/drivers/github/1.png diff --git a/public/img/drivers/github/2.png b/pages/public/img/drivers/github/2.png similarity index 100% rename from public/img/drivers/github/2.png rename to pages/public/img/drivers/github/2.png diff --git a/public/img/drivers/github/3.png b/pages/public/img/drivers/github/3.png similarity index 100% rename from public/img/drivers/github/3.png rename to pages/public/img/drivers/github/3.png diff --git a/public/img/drivers/google/google-00-d.png b/pages/public/img/drivers/google/google-00-d.png similarity index 100% rename from public/img/drivers/google/google-00-d.png rename to pages/public/img/drivers/google/google-00-d.png diff --git a/public/img/drivers/google/google-00-l.png b/pages/public/img/drivers/google/google-00-l.png similarity index 100% rename from public/img/drivers/google/google-00-l.png rename to pages/public/img/drivers/google/google-00-l.png diff --git a/public/img/drivers/google/google-01-01-d.png b/pages/public/img/drivers/google/google-01-01-d.png similarity index 100% rename from public/img/drivers/google/google-01-01-d.png rename to pages/public/img/drivers/google/google-01-01-d.png diff --git a/public/img/drivers/google/google-01-01-l.png b/pages/public/img/drivers/google/google-01-01-l.png similarity index 100% rename from public/img/drivers/google/google-01-01-l.png rename to pages/public/img/drivers/google/google-01-01-l.png diff --git a/public/img/drivers/google/google-01-02-d.png b/pages/public/img/drivers/google/google-01-02-d.png similarity index 100% rename from public/img/drivers/google/google-01-02-d.png rename to pages/public/img/drivers/google/google-01-02-d.png diff --git a/public/img/drivers/google/google-01-02-l.png b/pages/public/img/drivers/google/google-01-02-l.png similarity index 100% rename from public/img/drivers/google/google-01-02-l.png rename to pages/public/img/drivers/google/google-01-02-l.png diff --git a/public/img/drivers/google/google-02.png b/pages/public/img/drivers/google/google-02.png similarity index 100% rename from public/img/drivers/google/google-02.png rename to pages/public/img/drivers/google/google-02.png diff --git a/public/img/drivers/google/google-03.png b/pages/public/img/drivers/google/google-03.png similarity index 100% rename from public/img/drivers/google/google-03.png rename to pages/public/img/drivers/google/google-03.png diff --git a/public/img/drivers/google/google-04.png b/pages/public/img/drivers/google/google-04.png similarity index 100% rename from public/img/drivers/google/google-04.png rename to pages/public/img/drivers/google/google-04.png diff --git a/public/img/drivers/google/google-05-d.png b/pages/public/img/drivers/google/google-05-d.png similarity index 100% rename from public/img/drivers/google/google-05-d.png rename to pages/public/img/drivers/google/google-05-d.png diff --git a/public/img/drivers/google/google-05-l.png b/pages/public/img/drivers/google/google-05-l.png similarity index 100% rename from public/img/drivers/google/google-05-l.png rename to pages/public/img/drivers/google/google-05-l.png diff --git a/public/img/drivers/google/google-06-d.png b/pages/public/img/drivers/google/google-06-d.png similarity index 100% rename from public/img/drivers/google/google-06-d.png rename to pages/public/img/drivers/google/google-06-d.png diff --git a/public/img/drivers/google/google-06-l.png b/pages/public/img/drivers/google/google-06-l.png similarity index 100% rename from public/img/drivers/google/google-06-l.png rename to pages/public/img/drivers/google/google-06-l.png diff --git a/public/img/drivers/google/google-07-d.png b/pages/public/img/drivers/google/google-07-d.png similarity index 100% rename from public/img/drivers/google/google-07-d.png rename to pages/public/img/drivers/google/google-07-d.png diff --git a/public/img/drivers/google/google-07-l.png b/pages/public/img/drivers/google/google-07-l.png similarity index 100% rename from public/img/drivers/google/google-07-l.png rename to pages/public/img/drivers/google/google-07-l.png diff --git a/public/img/drivers/google/google-08-d.png b/pages/public/img/drivers/google/google-08-d.png similarity index 100% rename from public/img/drivers/google/google-08-d.png rename to pages/public/img/drivers/google/google-08-d.png diff --git a/public/img/drivers/google/google-08-l.png b/pages/public/img/drivers/google/google-08-l.png similarity index 100% rename from public/img/drivers/google/google-08-l.png rename to pages/public/img/drivers/google/google-08-l.png diff --git a/public/img/drivers/google/google-oauth-00.png b/pages/public/img/drivers/google/google-oauth-00.png similarity index 100% rename from public/img/drivers/google/google-oauth-00.png rename to pages/public/img/drivers/google/google-oauth-00.png diff --git a/public/img/drivers/google/google-oauth-01.png b/pages/public/img/drivers/google/google-oauth-01.png similarity index 100% rename from public/img/drivers/google/google-oauth-01.png rename to pages/public/img/drivers/google/google-oauth-01.png diff --git a/public/img/drivers/google/google-oauth-02.png b/pages/public/img/drivers/google/google-oauth-02.png similarity index 100% rename from public/img/drivers/google/google-oauth-02.png rename to pages/public/img/drivers/google/google-oauth-02.png diff --git a/public/img/drivers/google/google-oauth-03.png b/pages/public/img/drivers/google/google-oauth-03.png similarity index 100% rename from public/img/drivers/google/google-oauth-03.png rename to pages/public/img/drivers/google/google-oauth-03.png diff --git a/public/img/drivers/google/google-oauth-eng-00.png b/pages/public/img/drivers/google/google-oauth-eng-00.png similarity index 100% rename from public/img/drivers/google/google-oauth-eng-00.png rename to pages/public/img/drivers/google/google-oauth-eng-00.png diff --git a/public/img/drivers/google/google-oauth-eng-01.png b/pages/public/img/drivers/google/google-oauth-eng-01.png similarity index 100% rename from public/img/drivers/google/google-oauth-eng-01.png rename to pages/public/img/drivers/google/google-oauth-eng-01.png diff --git a/public/img/drivers/google/google-oauth-eng-02.png b/pages/public/img/drivers/google/google-oauth-eng-02.png similarity index 100% rename from public/img/drivers/google/google-oauth-eng-02.png rename to pages/public/img/drivers/google/google-oauth-eng-02.png diff --git a/public/img/drivers/google/google-oauth-eng-03.png b/pages/public/img/drivers/google/google-oauth-eng-03.png similarity index 100% rename from public/img/drivers/google/google-oauth-eng-03.png rename to pages/public/img/drivers/google/google-oauth-eng-03.png diff --git a/public/img/drivers/google/googledrive-dir.png b/pages/public/img/drivers/google/googledrive-dir.png similarity index 100% rename from public/img/drivers/google/googledrive-dir.png rename to pages/public/img/drivers/google/googledrive-dir.png diff --git a/public/img/drivers/halalcloud/halalcloud_add_authorization.png b/pages/public/img/drivers/halalcloud/halalcloud_add_authorization.png similarity index 100% rename from public/img/drivers/halalcloud/halalcloud_add_authorization.png rename to pages/public/img/drivers/halalcloud/halalcloud_add_authorization.png diff --git a/public/img/drivers/kodbox/kodbox_root_folder_path_en.jpg b/pages/public/img/drivers/kodbox/kodbox_root_folder_path_en.jpg similarity index 100% rename from public/img/drivers/kodbox/kodbox_root_folder_path_en.jpg rename to pages/public/img/drivers/kodbox/kodbox_root_folder_path_en.jpg diff --git a/public/img/drivers/kodbox/kodbox_root_folder_path_zh.jpg b/pages/public/img/drivers/kodbox/kodbox_root_folder_path_zh.jpg similarity index 100% rename from public/img/drivers/kodbox/kodbox_root_folder_path_zh.jpg rename to pages/public/img/drivers/kodbox/kodbox_root_folder_path_zh.jpg diff --git a/public/img/drivers/lanzou/ilanzou_folder.png b/pages/public/img/drivers/lanzou/ilanzou_folder.png similarity index 100% rename from public/img/drivers/lanzou/ilanzou_folder.png rename to pages/public/img/drivers/lanzou/ilanzou_folder.png diff --git a/public/img/drivers/lanzou/lanzou1.png b/pages/public/img/drivers/lanzou/lanzou1.png similarity index 100% rename from public/img/drivers/lanzou/lanzou1.png rename to pages/public/img/drivers/lanzou/lanzou1.png diff --git a/public/img/drivers/lanzou/lanzou2.gif b/pages/public/img/drivers/lanzou/lanzou2.gif similarity index 100% rename from public/img/drivers/lanzou/lanzou2.gif rename to pages/public/img/drivers/lanzou/lanzou2.gif diff --git a/public/img/drivers/lanzou/lanzou3.png b/pages/public/img/drivers/lanzou/lanzou3.png similarity index 100% rename from public/img/drivers/lanzou/lanzou3.png rename to pages/public/img/drivers/lanzou/lanzou3.png diff --git a/public/img/drivers/lanzou/lanzou_add.png b/pages/public/img/drivers/lanzou/lanzou_add.png similarity index 100% rename from public/img/drivers/lanzou/lanzou_add.png rename to pages/public/img/drivers/lanzou/lanzou_add.png diff --git a/public/img/drivers/lenovonasshare/lenovonasshare_fileid.png b/pages/public/img/drivers/lenovonasshare/lenovonasshare_fileid.png similarity index 100% rename from public/img/drivers/lenovonasshare/lenovonasshare_fileid.png rename to pages/public/img/drivers/lenovonasshare/lenovonasshare_fileid.png diff --git a/public/img/drivers/mediafire/mediafire-cookie.png b/pages/public/img/drivers/mediafire/mediafire-cookie.png similarity index 100% rename from public/img/drivers/mediafire/mediafire-cookie.png rename to pages/public/img/drivers/mediafire/mediafire-cookie.png diff --git a/public/img/drivers/mediafire/mediafire-sessiontoken.png b/pages/public/img/drivers/mediafire/mediafire-sessiontoken.png similarity index 100% rename from public/img/drivers/mediafire/mediafire-sessiontoken.png rename to pages/public/img/drivers/mediafire/mediafire-sessiontoken.png diff --git a/public/img/drivers/mediatrack-projectid.png b/pages/public/img/drivers/mediatrack-projectid.png similarity index 100% rename from public/img/drivers/mediatrack-projectid.png rename to pages/public/img/drivers/mediatrack-projectid.png diff --git a/public/img/drivers/mediatrack-rootid.png b/pages/public/img/drivers/mediatrack-rootid.png similarity index 100% rename from public/img/drivers/mediatrack-rootid.png rename to pages/public/img/drivers/mediatrack-rootid.png diff --git a/public/img/drivers/mediatrack-token.png b/pages/public/img/drivers/mediatrack-token.png similarity index 100% rename from public/img/drivers/mediatrack-token.png rename to pages/public/img/drivers/mediatrack-token.png diff --git a/public/img/drivers/mega/mega_2fa.png b/pages/public/img/drivers/mega/mega_2fa.png similarity index 100% rename from public/img/drivers/mega/mega_2fa.png rename to pages/public/img/drivers/mega/mega_2fa.png diff --git a/public/img/drivers/mega/mega_2fa_code.png b/pages/public/img/drivers/mega/mega_2fa_code.png similarity index 100% rename from public/img/drivers/mega/mega_2fa_code.png rename to pages/public/img/drivers/mega/mega_2fa_code.png diff --git a/public/img/drivers/mega/mega_add.png b/pages/public/img/drivers/mega/mega_add.png similarity index 100% rename from public/img/drivers/mega/mega_add.png rename to pages/public/img/drivers/mega/mega_add.png diff --git a/public/img/drivers/mopan/add-mopan.png b/pages/public/img/drivers/mopan/add-mopan.png similarity index 100% rename from public/img/drivers/mopan/add-mopan.png rename to pages/public/img/drivers/mopan/add-mopan.png diff --git a/public/img/drivers/offline_download_error.png b/pages/public/img/drivers/offline_download_error.png similarity index 100% rename from public/img/drivers/offline_download_error.png rename to pages/public/img/drivers/offline_download_error.png diff --git a/public/img/drivers/onedrive_app/od_app_ok.png b/pages/public/img/drivers/onedrive_app/od_app_ok.png similarity index 100% rename from public/img/drivers/onedrive_app/od_app_ok.png rename to pages/public/img/drivers/onedrive_app/od_app_ok.png diff --git a/public/img/drivers/onedrive_app/od_share_add.png b/pages/public/img/drivers/onedrive_app/od_share_add.png similarity index 100% rename from public/img/drivers/onedrive_app/od_share_add.png rename to pages/public/img/drivers/onedrive_app/od_share_add.png diff --git a/public/img/drivers/onedrive_app/onedrive_app0.png b/pages/public/img/drivers/onedrive_app/onedrive_app0.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app0.png rename to pages/public/img/drivers/onedrive_app/onedrive_app0.png diff --git a/public/img/drivers/onedrive_app/onedrive_app1.png b/pages/public/img/drivers/onedrive_app/onedrive_app1.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app1.png rename to pages/public/img/drivers/onedrive_app/onedrive_app1.png diff --git a/public/img/drivers/onedrive_app/onedrive_app3.png b/pages/public/img/drivers/onedrive_app/onedrive_app3.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app3.png rename to pages/public/img/drivers/onedrive_app/onedrive_app3.png diff --git a/public/img/drivers/onedrive_app/onedrive_app4.png b/pages/public/img/drivers/onedrive_app/onedrive_app4.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app4.png rename to pages/public/img/drivers/onedrive_app/onedrive_app4.png diff --git a/public/img/drivers/onedrive_app/onedrive_app5.png b/pages/public/img/drivers/onedrive_app/onedrive_app5.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app5.png rename to pages/public/img/drivers/onedrive_app/onedrive_app5.png diff --git a/public/img/drivers/onedrive_app/onedrive_app6.png b/pages/public/img/drivers/onedrive_app/onedrive_app6.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app6.png rename to pages/public/img/drivers/onedrive_app/onedrive_app6.png diff --git a/public/img/drivers/onedrive_app/onedrive_app7.png b/pages/public/img/drivers/onedrive_app/onedrive_app7.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app7.png rename to pages/public/img/drivers/onedrive_app/onedrive_app7.png diff --git a/public/img/drivers/onedrive_app/onedrive_app_Denmo.png b/pages/public/img/drivers/onedrive_app/onedrive_app_Denmo.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app_Denmo.png rename to pages/public/img/drivers/onedrive_app/onedrive_app_Denmo.png diff --git a/public/img/drivers/onedrive_app/onedrive_app_user.png b/pages/public/img/drivers/onedrive_app/onedrive_app_user.png similarity index 100% rename from public/img/drivers/onedrive_app/onedrive_app_user.png rename to pages/public/img/drivers/onedrive_app/onedrive_app_user.png diff --git a/public/img/drivers/pikpak.png b/pages/public/img/drivers/pikpak.png similarity index 100% rename from public/img/drivers/pikpak.png rename to pages/public/img/drivers/pikpak.png diff --git a/public/img/drivers/pikpak/pikpak-error-1.png b/pages/public/img/drivers/pikpak/pikpak-error-1.png similarity index 100% rename from public/img/drivers/pikpak/pikpak-error-1.png rename to pages/public/img/drivers/pikpak/pikpak-error-1.png diff --git a/public/img/drivers/pikpak/pikpak-error-2.png b/pages/public/img/drivers/pikpak/pikpak-error-2.png similarity index 100% rename from public/img/drivers/pikpak/pikpak-error-2.png rename to pages/public/img/drivers/pikpak/pikpak-error-2.png diff --git a/public/img/drivers/pikpak/pikpak-share-rootfolder.png b/pages/public/img/drivers/pikpak/pikpak-share-rootfolder.png similarity index 100% rename from public/img/drivers/pikpak/pikpak-share-rootfolder.png rename to pages/public/img/drivers/pikpak/pikpak-share-rootfolder.png diff --git a/public/img/drivers/pikpak/pikpak-share.png b/pages/public/img/drivers/pikpak/pikpak-share.png similarity index 100% rename from public/img/drivers/pikpak/pikpak-share.png rename to pages/public/img/drivers/pikpak/pikpak-share.png diff --git a/public/img/drivers/pikpak/pikpak-token-1.png b/pages/public/img/drivers/pikpak/pikpak-token-1.png similarity index 100% rename from public/img/drivers/pikpak/pikpak-token-1.png rename to pages/public/img/drivers/pikpak/pikpak-token-1.png diff --git a/public/img/drivers/pikpak/pikpak-token-2.png b/pages/public/img/drivers/pikpak/pikpak-token-2.png similarity index 100% rename from public/img/drivers/pikpak/pikpak-token-2.png rename to pages/public/img/drivers/pikpak/pikpak-token-2.png diff --git a/public/img/drivers/quark-rootid.png b/pages/public/img/drivers/quark-rootid.png similarity index 100% rename from public/img/drivers/quark-rootid.png rename to pages/public/img/drivers/quark-rootid.png diff --git a/public/img/drivers/quark.png b/pages/public/img/drivers/quark.png similarity index 100% rename from public/img/drivers/quark.png rename to pages/public/img/drivers/quark.png diff --git a/public/img/drivers/quark/quark_cookie.png b/pages/public/img/drivers/quark/quark_cookie.png similarity index 100% rename from public/img/drivers/quark/quark_cookie.png rename to pages/public/img/drivers/quark/quark_cookie.png diff --git a/public/img/drivers/quark/quark_fileid.png b/pages/public/img/drivers/quark/quark_fileid.png similarity index 100% rename from public/img/drivers/quark/quark_fileid.png rename to pages/public/img/drivers/quark/quark_fileid.png diff --git a/public/img/drivers/ref_token.png b/pages/public/img/drivers/ref_token.png similarity index 100% rename from public/img/drivers/ref_token.png rename to pages/public/img/drivers/ref_token.png diff --git a/public/img/drivers/s3/139.png b/pages/public/img/drivers/s3/139.png similarity index 100% rename from public/img/drivers/s3/139.png rename to pages/public/img/drivers/s3/139.png diff --git a/public/img/drivers/s3/al.png b/pages/public/img/drivers/s3/al.png similarity index 100% rename from public/img/drivers/s3/al.png rename to pages/public/img/drivers/s3/al.png diff --git a/public/img/drivers/s3/aws.png b/pages/public/img/drivers/s3/aws.png similarity index 100% rename from public/img/drivers/s3/aws.png rename to pages/public/img/drivers/s3/aws.png diff --git a/public/img/drivers/s3/b2.png b/pages/public/img/drivers/s3/b2.png similarity index 100% rename from public/img/drivers/s3/b2.png rename to pages/public/img/drivers/s3/b2.png diff --git a/public/img/drivers/s3/bd.png b/pages/public/img/drivers/s3/bd.png similarity index 100% rename from public/img/drivers/s3/bd.png rename to pages/public/img/drivers/s3/bd.png diff --git a/public/img/drivers/s3/cfr2.png b/pages/public/img/drivers/s3/cfr2.png similarity index 100% rename from public/img/drivers/s3/cfr2.png rename to pages/public/img/drivers/s3/cfr2.png diff --git a/public/img/drivers/s3/doge.png b/pages/public/img/drivers/s3/doge.png similarity index 100% rename from public/img/drivers/s3/doge.png rename to pages/public/img/drivers/s3/doge.png diff --git a/public/img/drivers/s3/googleStorage1.png b/pages/public/img/drivers/s3/googleStorage1.png similarity index 100% rename from public/img/drivers/s3/googleStorage1.png rename to pages/public/img/drivers/s3/googleStorage1.png diff --git a/public/img/drivers/s3/googleStorage2.png b/pages/public/img/drivers/s3/googleStorage2.png similarity index 100% rename from public/img/drivers/s3/googleStorage2.png rename to pages/public/img/drivers/s3/googleStorage2.png diff --git a/public/img/drivers/s3/googleStorage3.png b/pages/public/img/drivers/s3/googleStorage3.png similarity index 100% rename from public/img/drivers/s3/googleStorage3.png rename to pages/public/img/drivers/s3/googleStorage3.png diff --git a/public/img/drivers/s3/googleStorage4.png b/pages/public/img/drivers/s3/googleStorage4.png similarity index 100% rename from public/img/drivers/s3/googleStorage4.png rename to pages/public/img/drivers/s3/googleStorage4.png diff --git a/public/img/drivers/s3/hw.png b/pages/public/img/drivers/s3/hw.png similarity index 100% rename from public/img/drivers/s3/hw.png rename to pages/public/img/drivers/s3/hw.png diff --git a/public/img/drivers/s3/jd.png b/pages/public/img/drivers/s3/jd.png similarity index 100% rename from public/img/drivers/s3/jd.png rename to pages/public/img/drivers/s3/jd.png diff --git a/public/img/drivers/s3/mini-io.png b/pages/public/img/drivers/s3/mini-io.png similarity index 100% rename from public/img/drivers/s3/mini-io.png rename to pages/public/img/drivers/s3/mini-io.png diff --git a/public/img/drivers/s3/qn.png b/pages/public/img/drivers/s3/qn.png similarity index 100% rename from public/img/drivers/s3/qn.png rename to pages/public/img/drivers/s3/qn.png diff --git a/public/img/drivers/s3/sc.png b/pages/public/img/drivers/s3/sc.png similarity index 100% rename from public/img/drivers/s3/sc.png rename to pages/public/img/drivers/s3/sc.png diff --git a/public/img/drivers/s3/tx.png b/pages/public/img/drivers/s3/tx.png similarity index 100% rename from public/img/drivers/s3/tx.png rename to pages/public/img/drivers/s3/tx.png diff --git a/public/img/drivers/s3/ty.png b/pages/public/img/drivers/s3/ty.png similarity index 100% rename from public/img/drivers/s3/ty.png rename to pages/public/img/drivers/s3/ty.png diff --git a/public/img/drivers/s3/up.png b/pages/public/img/drivers/s3/up.png similarity index 100% rename from public/img/drivers/s3/up.png rename to pages/public/img/drivers/s3/up.png diff --git a/public/img/drivers/s3/v1andv2.png b/pages/public/img/drivers/s3/v1andv2.png similarity index 100% rename from public/img/drivers/s3/v1andv2.png rename to pages/public/img/drivers/s3/v1andv2.png diff --git a/public/img/drivers/s3/zijie_tos.png b/pages/public/img/drivers/s3/zijie_tos.png similarity index 100% rename from public/img/drivers/s3/zijie_tos.png rename to pages/public/img/drivers/s3/zijie_tos.png diff --git a/public/img/drivers/teambition-cookie.png b/pages/public/img/drivers/teambition-cookie.png similarity index 100% rename from public/img/drivers/teambition-cookie.png rename to pages/public/img/drivers/teambition-cookie.png diff --git a/public/img/drivers/teambition-id.png b/pages/public/img/drivers/teambition-id.png similarity index 100% rename from public/img/drivers/teambition-id.png rename to pages/public/img/drivers/teambition-id.png diff --git a/public/img/drivers/teldrive/fetch_teldrive_cookie.png b/pages/public/img/drivers/teldrive/fetch_teldrive_cookie.png similarity index 100% rename from public/img/drivers/teldrive/fetch_teldrive_cookie.png rename to pages/public/img/drivers/teldrive/fetch_teldrive_cookie.png diff --git a/public/img/drivers/terabox/terabox1.png b/pages/public/img/drivers/terabox/terabox1.png similarity index 100% rename from public/img/drivers/terabox/terabox1.png rename to pages/public/img/drivers/terabox/terabox1.png diff --git a/public/img/drivers/terabox/terabox2.png b/pages/public/img/drivers/terabox/terabox2.png similarity index 100% rename from public/img/drivers/terabox/terabox2.png rename to pages/public/img/drivers/terabox/terabox2.png diff --git a/public/img/drivers/terabox/terabox3.png b/pages/public/img/drivers/terabox/terabox3.png similarity index 100% rename from public/img/drivers/terabox/terabox3.png rename to pages/public/img/drivers/terabox/terabox3.png diff --git a/public/img/drivers/terabox/terabox_cookie.png b/pages/public/img/drivers/terabox/terabox_cookie.png similarity index 100% rename from public/img/drivers/terabox/terabox_cookie.png rename to pages/public/img/drivers/terabox/terabox_cookie.png diff --git a/public/img/drivers/terabox/terabox_f12.png b/pages/public/img/drivers/terabox/terabox_f12.png similarity index 100% rename from public/img/drivers/terabox/terabox_f12.png rename to pages/public/img/drivers/terabox/terabox_f12.png diff --git a/public/img/drivers/terabox/terabox_js_token.png b/pages/public/img/drivers/terabox/terabox_js_token.png similarity index 100% rename from public/img/drivers/terabox/terabox_js_token.png rename to pages/public/img/drivers/terabox/terabox_js_token.png diff --git a/public/img/drivers/tv_qrcode.png b/pages/public/img/drivers/tv_qrcode.png similarity index 100% rename from public/img/drivers/tv_qrcode.png rename to pages/public/img/drivers/tv_qrcode.png diff --git a/public/img/drivers/uc/uc_cookie.png b/pages/public/img/drivers/uc/uc_cookie.png similarity index 100% rename from public/img/drivers/uc/uc_cookie.png rename to pages/public/img/drivers/uc/uc_cookie.png diff --git a/public/img/drivers/uc/uc_fileid.png b/pages/public/img/drivers/uc/uc_fileid.png similarity index 100% rename from public/img/drivers/uc/uc_fileid.png rename to pages/public/img/drivers/uc/uc_fileid.png diff --git a/public/img/drivers/vtencent/vtencent_close.png b/pages/public/img/drivers/vtencent/vtencent_close.png similarity index 100% rename from public/img/drivers/vtencent/vtencent_close.png rename to pages/public/img/drivers/vtencent/vtencent_close.png diff --git a/public/img/drivers/vtencent/vtencent_cookie.png b/pages/public/img/drivers/vtencent/vtencent_cookie.png similarity index 100% rename from public/img/drivers/vtencent/vtencent_cookie.png rename to pages/public/img/drivers/vtencent/vtencent_cookie.png diff --git a/public/img/drivers/webdav/webdav_Security.png b/pages/public/img/drivers/webdav/webdav_Security.png similarity index 100% rename from public/img/drivers/webdav/webdav_Security.png rename to pages/public/img/drivers/webdav/webdav_Security.png diff --git a/public/img/drivers/weiyun/weiyun_cookie.png b/pages/public/img/drivers/weiyun/weiyun_cookie.png similarity index 100% rename from public/img/drivers/weiyun/weiyun_cookie.png rename to pages/public/img/drivers/weiyun/weiyun_cookie.png diff --git a/public/img/drivers/weiyun/weiyun_dir_id.png b/pages/public/img/drivers/weiyun/weiyun_dir_id.png similarity index 100% rename from public/img/drivers/weiyun/weiyun_dir_id.png rename to pages/public/img/drivers/weiyun/weiyun_dir_id.png diff --git a/public/img/drivers/weiyun/weiyun_fl_id.png b/pages/public/img/drivers/weiyun/weiyun_fl_id.png similarity index 100% rename from public/img/drivers/weiyun/weiyun_fl_id.png rename to pages/public/img/drivers/weiyun/weiyun_fl_id.png diff --git a/public/img/drivers/wopan/add-wopan.png b/pages/public/img/drivers/wopan/add-wopan.png similarity index 100% rename from public/img/drivers/wopan/add-wopan.png rename to pages/public/img/drivers/wopan/add-wopan.png diff --git a/public/img/drivers/wopan/wopan-req.png b/pages/public/img/drivers/wopan/wopan-req.png similarity index 100% rename from public/img/drivers/wopan/wopan-req.png rename to pages/public/img/drivers/wopan/wopan-req.png diff --git a/public/img/drivers/wopan/wopan-resp.png b/pages/public/img/drivers/wopan/wopan-resp.png similarity index 100% rename from public/img/drivers/wopan/wopan-resp.png rename to pages/public/img/drivers/wopan/wopan-resp.png diff --git a/public/img/drivers/wopan/wopan-session-storage.png b/pages/public/img/drivers/wopan/wopan-session-storage.png similarity index 100% rename from public/img/drivers/wopan/wopan-session-storage.png rename to pages/public/img/drivers/wopan/wopan-session-storage.png diff --git a/public/img/drivers/xunlei/SMS.png b/pages/public/img/drivers/xunlei/SMS.png similarity index 100% rename from public/img/drivers/xunlei/SMS.png rename to pages/public/img/drivers/xunlei/SMS.png diff --git a/public/img/drivers/xunlei/input.png b/pages/public/img/drivers/xunlei/input.png similarity index 100% rename from public/img/drivers/xunlei/input.png rename to pages/public/img/drivers/xunlei/input.png diff --git a/public/img/drivers/xunlei/output.png b/pages/public/img/drivers/xunlei/output.png similarity index 100% rename from public/img/drivers/xunlei/output.png rename to pages/public/img/drivers/xunlei/output.png diff --git a/public/img/drivers/xunlei/storage_error.png b/pages/public/img/drivers/xunlei/storage_error.png similarity index 100% rename from public/img/drivers/xunlei/storage_error.png rename to pages/public/img/drivers/xunlei/storage_error.png diff --git a/public/img/drivers/xunlei/x3.png b/pages/public/img/drivers/xunlei/x3.png similarity index 100% rename from public/img/drivers/xunlei/x3.png rename to pages/public/img/drivers/xunlei/x3.png diff --git a/public/img/drivers/xunlei/x4.png b/pages/public/img/drivers/xunlei/x4.png similarity index 100% rename from public/img/drivers/xunlei/x4.png rename to pages/public/img/drivers/xunlei/x4.png diff --git a/public/img/drivers/xunlei/x5.png b/pages/public/img/drivers/xunlei/x5.png similarity index 100% rename from public/img/drivers/xunlei/x5.png rename to pages/public/img/drivers/xunlei/x5.png diff --git a/public/img/drivers/xunlei/x6.png b/pages/public/img/drivers/xunlei/x6.png similarity index 100% rename from public/img/drivers/xunlei/x6.png rename to pages/public/img/drivers/xunlei/x6.png diff --git a/public/img/drivers/xunlei/x_br_add1.png b/pages/public/img/drivers/xunlei/x_br_add1.png similarity index 100% rename from public/img/drivers/xunlei/x_br_add1.png rename to pages/public/img/drivers/xunlei/x_br_add1.png diff --git a/public/img/drivers/xunlei/x_br_add2.png b/pages/public/img/drivers/xunlei/x_br_add2.png similarity index 100% rename from public/img/drivers/xunlei/x_br_add2.png rename to pages/public/img/drivers/xunlei/x_br_add2.png diff --git a/public/img/drivers/xunlei/x_br_error.png b/pages/public/img/drivers/xunlei/x_br_error.png similarity index 100% rename from public/img/drivers/xunlei/x_br_error.png rename to pages/public/img/drivers/xunlei/x_br_error.png diff --git a/public/img/drivers/xunlei/x_br_foled.png b/pages/public/img/drivers/xunlei/x_br_foled.png similarity index 100% rename from public/img/drivers/xunlei/x_br_foled.png rename to pages/public/img/drivers/xunlei/x_br_foled.png diff --git a/public/img/drivers/xunlei/xlx_name.jpg b/pages/public/img/drivers/xunlei/xlx_name.jpg similarity index 100% rename from public/img/drivers/xunlei/xlx_name.jpg rename to pages/public/img/drivers/xunlei/xlx_name.jpg diff --git a/public/img/drivers/xunlei/xlx_z1.jpg b/pages/public/img/drivers/xunlei/xlx_z1.jpg similarity index 100% rename from public/img/drivers/xunlei/xlx_z1.jpg rename to pages/public/img/drivers/xunlei/xlx_z1.jpg diff --git a/public/img/drivers/xunlei/xlx_z2.jpg b/pages/public/img/drivers/xunlei/xlx_z2.jpg similarity index 100% rename from public/img/drivers/xunlei/xlx_z2.jpg rename to pages/public/img/drivers/xunlei/xlx_z2.jpg diff --git a/public/img/faq/123error.png b/pages/public/img/faq/123error.png similarity index 100% rename from public/img/faq/123error.png rename to pages/public/img/faq/123error.png diff --git a/public/img/faq/add.jpeg b/pages/public/img/faq/add.jpeg similarity index 100% rename from public/img/faq/add.jpeg rename to pages/public/img/faq/add.jpeg diff --git a/public/img/faq/alist-admin.gif b/pages/public/img/faq/alist-admin.gif similarity index 100% rename from public/img/faq/alist-admin.gif rename to pages/public/img/faq/alist-admin.gif diff --git a/public/img/faq/alist-admin.png b/pages/public/img/faq/alist-admin.png similarity index 100% rename from public/img/faq/alist-admin.png rename to pages/public/img/faq/alist-admin.png diff --git a/public/img/faq/download.png b/pages/public/img/faq/download.png similarity index 100% rename from public/img/faq/download.png rename to pages/public/img/faq/download.png diff --git a/public/img/faq/error.png b/pages/public/img/faq/error.png similarity index 100% rename from public/img/faq/error.png rename to pages/public/img/faq/error.png diff --git a/public/img/guide/installation/clawcloud-01.png b/pages/public/img/guide/installation/clawcloud-01.png similarity index 100% rename from public/img/guide/installation/clawcloud-01.png rename to pages/public/img/guide/installation/clawcloud-01.png diff --git a/public/img/guide/installation/clawcloud-run.svg b/pages/public/img/guide/installation/clawcloud-run.svg similarity index 100% rename from public/img/guide/installation/clawcloud-run.svg rename to pages/public/img/guide/installation/clawcloud-run.svg diff --git a/public/img/guide/migrate/aliyun_remove1.png b/pages/public/img/guide/migrate/aliyun_remove1.png similarity index 100% rename from public/img/guide/migrate/aliyun_remove1.png rename to pages/public/img/guide/migrate/aliyun_remove1.png diff --git a/public/img/guide/migrate/aliyun_remove2.png b/pages/public/img/guide/migrate/aliyun_remove2.png similarity index 100% rename from public/img/guide/migrate/aliyun_remove2.png rename to pages/public/img/guide/migrate/aliyun_remove2.png diff --git a/public/img/guide/migrate/aliyun_remove3.png b/pages/public/img/guide/migrate/aliyun_remove3.png similarity index 100% rename from public/img/guide/migrate/aliyun_remove3.png rename to pages/public/img/guide/migrate/aliyun_remove3.png diff --git a/public/img/guide/migrate/aliyun_remove4.jpg b/pages/public/img/guide/migrate/aliyun_remove4.jpg similarity index 100% rename from public/img/guide/migrate/aliyun_remove4.jpg rename to pages/public/img/guide/migrate/aliyun_remove4.jpg diff --git a/public/img/guide/migrate/baidu_remove1.png b/pages/public/img/guide/migrate/baidu_remove1.png similarity index 100% rename from public/img/guide/migrate/baidu_remove1.png rename to pages/public/img/guide/migrate/baidu_remove1.png diff --git a/public/img/guide/migrate/baidu_remove2.png b/pages/public/img/guide/migrate/baidu_remove2.png similarity index 100% rename from public/img/guide/migrate/baidu_remove2.png rename to pages/public/img/guide/migrate/baidu_remove2.png diff --git a/public/img/guide/migrate/baidu_remove3.jpg b/pages/public/img/guide/migrate/baidu_remove3.jpg similarity index 100% rename from public/img/guide/migrate/baidu_remove3.jpg rename to pages/public/img/guide/migrate/baidu_remove3.jpg diff --git a/public/img/guide/migrate/google_remove1.png b/pages/public/img/guide/migrate/google_remove1.png similarity index 100% rename from public/img/guide/migrate/google_remove1.png rename to pages/public/img/guide/migrate/google_remove1.png diff --git a/public/img/guide/migrate/google_remove2.png b/pages/public/img/guide/migrate/google_remove2.png similarity index 100% rename from public/img/guide/migrate/google_remove2.png rename to pages/public/img/guide/migrate/google_remove2.png diff --git a/public/img/guide/migrate/google_remove3.png b/pages/public/img/guide/migrate/google_remove3.png similarity index 100% rename from public/img/guide/migrate/google_remove3.png rename to pages/public/img/guide/migrate/google_remove3.png diff --git a/public/img/guide/migrate/odb_remove1.jpg b/pages/public/img/guide/migrate/odb_remove1.jpg similarity index 100% rename from public/img/guide/migrate/odb_remove1.jpg rename to pages/public/img/guide/migrate/odb_remove1.jpg diff --git a/public/img/guide/migrate/odp_remove1.jpg b/pages/public/img/guide/migrate/odp_remove1.jpg similarity index 100% rename from public/img/guide/migrate/odp_remove1.jpg rename to pages/public/img/guide/migrate/odp_remove1.jpg diff --git a/public/img/guide/reverse_proxy/bt_delete_default_config_01.png b/pages/public/img/guide/reverse_proxy/bt_delete_default_config_01.png similarity index 100% rename from public/img/guide/reverse_proxy/bt_delete_default_config_01.png rename to pages/public/img/guide/reverse_proxy/bt_delete_default_config_01.png diff --git a/public/img/guide/reverse_proxy/bt_delete_default_config_02.png b/pages/public/img/guide/reverse_proxy/bt_delete_default_config_02.png similarity index 100% rename from public/img/guide/reverse_proxy/bt_delete_default_config_02.png rename to pages/public/img/guide/reverse_proxy/bt_delete_default_config_02.png diff --git a/public/img/guide/reverse_proxy/bt_new_website.png b/pages/public/img/guide/reverse_proxy/bt_new_website.png similarity index 100% rename from public/img/guide/reverse_proxy/bt_new_website.png rename to pages/public/img/guide/reverse_proxy/bt_new_website.png diff --git a/public/img/guide/reverse_proxy/bt_new_website_01.png b/pages/public/img/guide/reverse_proxy/bt_new_website_01.png similarity index 100% rename from public/img/guide/reverse_proxy/bt_new_website_01.png rename to pages/public/img/guide/reverse_proxy/bt_new_website_01.png diff --git a/public/img/guide/reverse_proxy/bt_reverse_proxy.png b/pages/public/img/guide/reverse_proxy/bt_reverse_proxy.png similarity index 100% rename from public/img/guide/reverse_proxy/bt_reverse_proxy.png rename to pages/public/img/guide/reverse_proxy/bt_reverse_proxy.png diff --git a/public/img/guide/webdav/es-android.png b/pages/public/img/guide/webdav/es-android.png similarity index 100% rename from public/img/guide/webdav/es-android.png rename to pages/public/img/guide/webdav/es-android.png diff --git a/public/img/guide/webdav/es-ios.png b/pages/public/img/guide/webdav/es-ios.png similarity index 100% rename from public/img/guide/webdav/es-ios.png rename to pages/public/img/guide/webdav/es-ios.png diff --git a/public/img/guide/webdav/fileball.png b/pages/public/img/guide/webdav/fileball.png similarity index 100% rename from public/img/guide/webdav/fileball.png rename to pages/public/img/guide/webdav/fileball.png diff --git a/public/img/guide/webdav/infuse.png b/pages/public/img/guide/webdav/infuse.png similarity index 100% rename from public/img/guide/webdav/infuse.png rename to pages/public/img/guide/webdav/infuse.png diff --git a/public/img/guide/webdav/nas.png b/pages/public/img/guide/webdav/nas.png similarity index 100% rename from public/img/guide/webdav/nas.png rename to pages/public/img/guide/webdav/nas.png diff --git a/public/img/guide/webdav/nplayer.png b/pages/public/img/guide/webdav/nplayer.png similarity index 100% rename from public/img/guide/webdav/nplayer.png rename to pages/public/img/guide/webdav/nplayer.png diff --git a/public/img/guide/webdav/potplayer.png b/pages/public/img/guide/webdav/potplayer.png similarity index 100% rename from public/img/guide/webdav/potplayer.png rename to pages/public/img/guide/webdav/potplayer.png diff --git a/public/img/guide/webdav/reex.png b/pages/public/img/guide/webdav/reex.png similarity index 100% rename from public/img/guide/webdav/reex.png rename to pages/public/img/guide/webdav/reex.png diff --git a/public/img/koyeb/db_host.png b/pages/public/img/koyeb/db_host.png similarity index 100% rename from public/img/koyeb/db_host.png rename to pages/public/img/koyeb/db_host.png diff --git a/public/img/koyeb/deployment.png b/pages/public/img/koyeb/deployment.png similarity index 100% rename from public/img/koyeb/deployment.png rename to pages/public/img/koyeb/deployment.png diff --git a/public/img/koyeb/environment.png b/pages/public/img/koyeb/environment.png similarity index 100% rename from public/img/koyeb/environment.png rename to pages/public/img/koyeb/environment.png diff --git a/public/img/koyeb/file.png b/pages/public/img/koyeb/file.png similarity index 100% rename from public/img/koyeb/file.png rename to pages/public/img/koyeb/file.png diff --git a/public/img/koyeb/success.png b/pages/public/img/koyeb/success.png similarity index 100% rename from public/img/koyeb/success.png rename to pages/public/img/koyeb/success.png diff --git a/public/img/truenas/InstallCustomAppApplicationName.png b/pages/public/img/truenas/InstallCustomAppApplicationName.png similarity index 100% rename from public/img/truenas/InstallCustomAppApplicationName.png rename to pages/public/img/truenas/InstallCustomAppApplicationName.png diff --git a/public/img/truenas/InstallCustomAppContainerEntrypoint.png b/pages/public/img/truenas/InstallCustomAppContainerEntrypoint.png similarity index 100% rename from public/img/truenas/InstallCustomAppContainerEntrypoint.png rename to pages/public/img/truenas/InstallCustomAppContainerEntrypoint.png diff --git a/public/img/truenas/InstallCustomAppImageConfiguration.png b/pages/public/img/truenas/InstallCustomAppImageConfiguration.png similarity index 100% rename from public/img/truenas/InstallCustomAppImageConfiguration.png rename to pages/public/img/truenas/InstallCustomAppImageConfiguration.png diff --git a/public/img/truenas/InstallCustomAppNetworkConfiguration.png b/pages/public/img/truenas/InstallCustomAppNetworkConfiguration.png similarity index 100% rename from public/img/truenas/InstallCustomAppNetworkConfiguration.png rename to pages/public/img/truenas/InstallCustomAppNetworkConfiguration.png diff --git a/public/img/truenas/InstallCustomAppPortalConfiguration.png b/pages/public/img/truenas/InstallCustomAppPortalConfiguration.png similarity index 100% rename from public/img/truenas/InstallCustomAppPortalConfiguration.png rename to pages/public/img/truenas/InstallCustomAppPortalConfiguration.png diff --git a/public/img/truenas/InstallCustomAppSecurityContextConfiguration.png b/pages/public/img/truenas/InstallCustomAppSecurityContextConfiguration.png similarity index 100% rename from public/img/truenas/InstallCustomAppSecurityContextConfiguration.png rename to pages/public/img/truenas/InstallCustomAppSecurityContextConfiguration.png diff --git a/public/img/truenas/InstallCustomAppStorageConfiguration.png b/pages/public/img/truenas/InstallCustomAppStorageConfiguration.png similarity index 100% rename from public/img/truenas/InstallCustomAppStorageConfiguration.png rename to pages/public/img/truenas/InstallCustomAppStorageConfiguration.png diff --git a/public/img/truenas/InstallCustomResourcesConfiguration.png b/pages/public/img/truenas/InstallCustomResourcesConfiguration.png similarity index 100% rename from public/img/truenas/InstallCustomResourcesConfiguration.png rename to pages/public/img/truenas/InstallCustomResourcesConfiguration.png diff --git a/public/img/worker/create_app1.png b/pages/public/img/worker/create_app1.png similarity index 100% rename from public/img/worker/create_app1.png rename to pages/public/img/worker/create_app1.png diff --git a/public/img/worker/create_app2.png b/pages/public/img/worker/create_app2.png similarity index 100% rename from public/img/worker/create_app2.png rename to pages/public/img/worker/create_app2.png diff --git a/public/img/worker/create_app3.png b/pages/public/img/worker/create_app3.png similarity index 100% rename from public/img/worker/create_app3.png rename to pages/public/img/worker/create_app3.png diff --git a/public/img/worker/create_app4.png b/pages/public/img/worker/create_app4.png similarity index 100% rename from public/img/worker/create_app4.png rename to pages/public/img/worker/create_app4.png diff --git a/public/img/worker/create_app5.png b/pages/public/img/worker/create_app5.png similarity index 100% rename from public/img/worker/create_app5.png rename to pages/public/img/worker/create_app5.png diff --git a/public/img/worker/create_app6.png b/pages/public/img/worker/create_app6.png similarity index 100% rename from public/img/worker/create_app6.png rename to pages/public/img/worker/create_app6.png diff --git a/public/img/worker/edgeone_ui1.png b/pages/public/img/worker/edgeone_ui1.png similarity index 100% rename from public/img/worker/edgeone_ui1.png rename to pages/public/img/worker/edgeone_ui1.png diff --git a/public/img/worker/edgeone_ui2.png b/pages/public/img/worker/edgeone_ui2.png similarity index 100% rename from public/img/worker/edgeone_ui2.png rename to pages/public/img/worker/edgeone_ui2.png diff --git a/public/img/worker/edgeone_ui3.png b/pages/public/img/worker/edgeone_ui3.png similarity index 100% rename from public/img/worker/edgeone_ui3.png rename to pages/public/img/worker/edgeone_ui3.png diff --git a/assets/logo.svg b/pages/public/logo.svg similarity index 100% rename from assets/logo.svg rename to pages/public/logo.svg diff --git a/assets/logo.webp b/pages/public/logo.webp similarity index 100% rename from assets/logo.webp rename to pages/public/logo.webp diff --git a/pages/seeds/background.md b/pages/seeds/background.md index 1d360058a..d4af69b4f 100644 --- a/pages/seeds/background.md +++ b/pages/seeds/background.md @@ -1,35 +1,18 @@ --- -title: - en: Background - zh-CN: 需求背景 categories: - seeds top: 990 --- -## 需求概述 { lang="zh-CN" } +# 需求背景 -## Overview { lang="en" } - -::::: zh-CN +## 需求概述 1. 用户上传文件到网盘、或在本地/网盘上,可对单个或多个文件生成种子,用于分享文件信息,以及文件丢失后重新上传。 2. 用户可分享种子,跨网盘、跨用户、跨设备进行离线秒传、分享文件、备份释放空间。 3. 通用 BT 客户端可通过服务器代理网盘驱动下载种子内文件;兼容 OSS 的客户端可秒传或经服务器代理下载。 - ::::: - -::::: en - -1. Users can generate a seed for one or more files — on local disk or on a drive — to share file information, or to re-upload after the files are lost. -2. Users can share seeds to perform offline rapid upload, file sharing, backup, and space release across drives, users, and devices. -3. Generic BT clients can download the files inside a seed through a server proxy over the drive; OSS-compatible clients can rapid-upload or download via the server proxy. - ::::: - -## 术语表 { lang="zh-CN" } - -## Glossary { lang="en" } -::::: zh-CN +## 术语表 | 术语 | 说明 | | ------------------------- | ---------------------------------------------------------------------------------- | @@ -44,30 +27,7 @@ top: 990 | 中转(Relay / Transit) | 先同步保存到中间网盘,再服务器端复制到最终目标 | | 渠道(Channel) | 种子在某个网盘驱动上的挂载位置(`driver` + `mount_path`) | -::::: - -::::: en - -| Term | Meaning | -| --------------- | ---------------------------------------------------------------------------------------------------- | -| Transfer Seed | A portable file-metadata system built on three sidecar formats | -| Sidecar | A companion file alongside the main file, e.g. `movie.mp4.torrent` | -| Seed | An `openlist-sharing-seed` v1 document describing path, size, hashes, and sources for a set of files | -| Hash Matrix | Specifies which hashes to compute per file (md5/sha1/sha256 × whole/pieces) | -| Piece | A block produced by splitting a file at `piece_size`; each piece has its own hash | -| Whole hash | A single hash computed over the entire file | -| Piece hashes | An array of hashes, one per piece | -| Rapid upload | Reusing known hashes so a drive completes instantly without re-sending content | -| Relay / Transit | Save synchronously to an intermediate drive, then copy server-side to the final drive | -| Channel | Where a seed is mounted on a drive (`driver` + `mount_path`) | - -::::: - -## 三种格式的定位 { lang="zh-CN" } - -## Format roles { lang="en" } - -::::: zh-CN +## 三种格式的定位 | 格式 | 后缀 | 编码 | 信息完整度 | 主要用途 | | --------------------- | ---------- | ----------- | ------------------------------------ | ------------------------------- | @@ -76,35 +36,10 @@ top: 990 | CAS | `.cas` | Base64 JSON | 低,仅秒传所需哈希 | 天翼云 189pc 秒传 | 三者是同一份文件元数据的不同投影,`.oss` 是信息超集,`.torrent` 与 `.cas` 是其语义投影(详见[设计理念](/seeds/design))。 -::::: - -::::: en - -| Format | Suffix | Encoding | Completeness | Primary use | -| --------------------- | ---------- | ----------- | --------------------------------------------------------- | -------------------------------------------- | -| OpenList Sharing Seed | `.oss` | JSON | Highest, losslessly carries all fields | OpenList-internal share / edit / recalculate | -| BitTorrent | `.torrent` | bencode | Medium, standard fields + `x-openlist` lossless extension | Generic BT clients / offline tools | -| CAS | `.cas` | Base64 JSON | Low, only rapid-upload hashes | 189pc rapid upload | - -The three are projections of the same file metadata: `.oss` is the superset, while `.torrent` and `.cas` are its semantic projections (see [Design principles](/seeds/design)). -::::: -## 典型应用场景 { lang="zh-CN" } - -## Typical scenarios { lang="en" } - -::::: zh-CN +## 典型应用场景 1. **跨盘迁移**:用户要把阿里云盘的文件迁到天翼云盘。生成含 SHA1 + MD5 + 分片哈希的种子,目标盘命中哈希即可秒传,无需下载再上传。 2. **分享给他人**:分享 `.oss` 或 `.torrent` 给好友,好友在其自己的网盘上秒传/离线下载,无需占用分享者的出流量。 3. **备份与释放空间**:把文件生成种子后删除原文件,需要时用种子重新秒传回来。 4. **断点校验**:种子的逐片哈希可用于校验已下载文件是否完整,支持按片定位损坏位置。 - ::::: - -::::: en - -1. **Cross-drive migration**: move files from Aliyundrive to 189pc. A seed carrying SHA1 + MD5 + piece hashes lets the destination hit the hash and rapid-upload without a full download-then-upload. -2. **Sharing**: share a `.oss` or `.torrent` so a friend rapid-uploads or offline-downloads on their own drive without consuming the sharer's egress. -3. **Backup and space release**: generate a seed, delete the original, and re-rapid-upload it later from the seed. -4. **Integrity verification**: per-piece hashes verify whether a downloaded file is complete and locate damaged pieces. - ::::: diff --git a/pages/seeds/design.md b/pages/seeds/design.md index 07c5faf98..ceaeef1b8 100644 --- a/pages/seeds/design.md +++ b/pages/seeds/design.md @@ -1,37 +1,20 @@ --- -title: - en: Design principles - zh-CN: 设计理念 categories: - seeds top: 980 --- -## 设计目标 { lang="zh-CN" } +# 设计理念 -## Goals { lang="en" } - -::::: zh-CN +## 设计目标 1. **可移植**:三种侧车格式可相互转换,跨网盘、跨用户、跨设备分享文件信息。 2. **复用哈希避免重复下载**:依据各网盘提供的哈希,生成时直接复用或流式计算。 3. **兼容第三方**:`.torrent` 遵循 BT v1 规范,`.cas` 单文件五字段与参考项目字节级兼容。 4. **安全有界**:所有解析受字节数、文件数、深度、路径穿越限制,种子内不序列化凭据。 - ::::: - -::::: en - -1. **Portable**: three convertible sidecar formats for sharing file information across drives, users, and devices. -2. **Reuse hashes to avoid redundant downloads**: reuse drive-provided hashes or stream-hash files. -3. **Third-party compatible**: `.torrent` follows BT v1, and the `.cas` single-file five fields are byte-compatible with the reference project. -4. **Safe and bounded**: parsing is bounded by byte/file/depth/path-traversal limits, and seeds never serialize credentials. - ::::: -## 三格式关系 { lang="zh-CN" } +## 三格式关系 -## Format relationships { lang="en" } - -::::: zh-CN 三种格式是同一份文件元数据的不同投影,`.oss` 信息最全,`.torrent` 与 `.cas` 是它的语义投影: - `.oss` ⊇ `.torrent`:`.oss` 含 BT 必要信息(SHA-1 完整 + 分片)时可转 `.torrent`;`.torrent` 无论如何都可转 `.oss`。 @@ -48,32 +31,7 @@ graph TD CAS -.->|缺 SHA1 分片,不可直接转| TORRENT ``` -::::: - -::::: en -The three formats are different projections of the same file metadata. `.oss` is the most complete; `.torrent` and `.cas` are its semantic projections: - -- `.oss` ⊇ `.torrent`: `.oss` converts to `.torrent` when it has the required BT info (SHA-1 whole + pieces); `.torrent` always converts to `.oss`. -- `.oss` ⊇ `.cas`: `.oss` converts to `.cas` when it has whole + piece MD5; `.cas` always converts to `.oss`. -- Conversion diagnostics report exactly what is missing. - -```mermaid -graph TD - OSS[.oss most complete] -->|has BT-required info| TORRENT[.torrent] - TORRENT -->|always convertible| OSS - OSS -->|has whole + piece MD5| CAS[.cas] - CAS -->|always convertible| OSS - TORRENT -.->|has x-cas extension| CAS - CAS -.->|missing SHA1 pieces, not directly convertible| TORRENT -``` - -::::: - -## 种子设计结构 { lang="zh-CN" } - -## Seed design { lang="en" } - -::::: zh-CN +## 种子设计结构 ### `.oss` 文件结构 @@ -129,71 +87,8 @@ Base64 编码的 JSON,兼容参考项目的五字段单文件格式,并扩 - 单文件时使用顶层五字段(与参考项目字节级兼容);多文件时使用 `files` 数组。 - `slice_md5s` / `slice_size` 为可选扩展,保存逐片 MD5 列表,供天翼云秒传复用。 - 三方 CAS 客户端使用标准 JSON 解析,忽略额外字段,向后兼容。 - ::::: - -::::: en - -### `.oss` structure - -`.oss` is the most complete JSON container: - -| Field | Description | -| --------------------------- | ----------------------------------------------------------------- | -| `format` / `version` | fixed `openlist-sharing-seed` / `1` | -| `name` | seed name (editable, auto-derived if empty) | -| `comment` | overall comment | -| `created_at` / `created_by` | creation time / creator | -| `piece_size` | piece size (default 10 MiB) | -| `trackers` | tracker list | -| `channels` | channels (`driver` + optional `mount_path`), public metadata only | -| `files` | file array | - -Each file (`files[]`) carries: - -| Field | Description | -| ----------------------------------- | ----------------------------------------------- | -| `path` / `size` / `modified` | path / size / mtime | -| `comment` | per-file comment | -| `hashes.md5` / `sha1` / `sha256` | whole-file hashes | -| `hashes.pieces` | per-algorithm piece hashes | -| `sources` | public direct (`/d/`) or share (`/sd/`) sources | -| `cas_slice_md5` / `cas_create_time` | CAS aggregate slice MD5 / create time | -| `missing_channels` | drives that failed rapid upload | - -### `.torrent` structure - -A standard BitTorrent v1 file whose info dict holds `piece length` and `pieces` (SHA-1), plus two extension keys: - -- `x-openlist`: the full OpenList seed (MD5/SHA256, comments, channels, shares, etc.). -- `x-cas`: 189pc CAS info (`file_md5` / `slice_md5` / `slice_size`). - -### `.cas` structure -A base64-encoded JSON, compatible with the reference five-field single-file payload and extended for multiple files: - -```json -{ - "name": "example", - "size": 12345, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "sliceMd5": "…", - "create_time": "1720000000", - "slice_md5s": ["…", "…"], - "slice_size": 10485760, - "files": [{ "name": "a.txt", "size": 100, "md5": "…", "sliceMd5": "…", "create_time": "…" }] -} -``` - -- Single file uses the top-level five fields (byte-compatible with the reference project); multiple files use the `files` array. -- `slice_md5s` / `slice_size` are optional extensions preserving the per-piece MD5 list for 189pc rapid upload. -- Third-party CAS clients use standard JSON parsing and ignore extra fields, remaining backward compatible. - ::::: - -## 字段级规范与安全边界 { lang="zh-CN" } - -## Field spec and safety bounds { lang="en" } - -::::: zh-CN +## 字段级规范与安全边界 所有入站种子(无论来源是 `.oss` / `.torrent` / `.cas` 还是任意 JSON)都会先经过**规范化(normalize)**,在解析阶段即施加以下硬性限制: @@ -218,40 +113,8 @@ A base64-encoded JSON, compatible with the reference five-field single-file payl - 缺少 `format` / `version` 时补齐为 `openlist-sharing-seed` / `1`。 - 缺少 `created_at` / `created_by` 时补当前时间 / `OpenList`。 - 重复文件路径直接报错。 - ::::: - -::::: en - -Every inbound seed (regardless of whether it comes from `.oss` / `.torrent` / `.cas` or arbitrary JSON) first passes through **normalization**, which enforces these hard limits at parse time: - -| Constraint | Value | -| ------------------------- | ------------------------------------------------------------------ | -| `piece_size` valid range | 16 KiB ~ 64 MiB (default 10 MiB) | -| File count | 1 ~ 100,000 | -| Max per-file path length | 4096 bytes | -| Max path depth | 64 levels | -| Piece hash count | must equal `ceil(size / piece_size)` (0 for empty files) | -| Max bencode nesting depth | 32 | -| Max bencode item count | 100,000 | -| Path safety | reject `\0`, backslash `\`, leading `/`, empty segments, `.`, `..` | -| Name safety | reject empty, `.`, `..`, or names containing `/` or `\` | -| `sources` URL | must be absolute with a scheme | -| `channels.mount_path` | reject `?`, `#`, `\0` | - -Seeds **never serialize credentials** (e.g. `cookie`, `access_token`); `channels` keeps only public metadata like `driver` and `mount_path`. Normalization also applies these fixes: - -- Hashes are lowercased; invalid hashes error (md5 32, sha1 40, sha256 64 hex chars). -- Missing `name` falls back to the first segment of `files[0].path`. -- Missing `format` / `version` default to `openlist-sharing-seed` / `1`. -- Missing `created_at` / `created_by` default to now / `OpenList`. -- Duplicate file paths error. - ::::: - -## 哈希矩阵设计 { lang="zh-CN" } - -## Hash matrix design { lang="en" } -::::: zh-CN +## 哈希矩阵设计 哈希矩阵是一个 3×2 的布尔选择,指定为每个文件计算哪些哈希: @@ -273,37 +136,8 @@ Seeds **never serialize credentials** (e.g. `cookie`, `access_token`); `channels 3. **复用驱动哈希**:生成向导会先调用 `capabilities` 预检,把驱动已在列表里提供的哈希默认勾选,从而避免重复下载计算。 矩阵决定生成时是否需要下载:只要矩阵要求分片哈希(`pieces`),网盘列表通常不提供分片哈希,就必须下载流式计算;整文件哈希缺一也需下载。 -::::: -::::: en - -The hash matrix is a 3×2 boolean selection specifying which hashes to compute per file: - -```json -{ - "md5": { "whole": true, "pieces": true }, - "sha1": { "whole": true, "pieces": true }, - "sha256": { "whole": true, "pieces": true } -} -``` - -- **whole**: a single whole-file hash (`hashes.md5` / `sha1` / `sha256`). -- **pieces**: per-piece hash arrays (`hashes.pieces.md5[]` / `sha1[]` / `sha256[]`). - -Normalization rules: - -1. **All-empty means all-on**: when all six flags are `false`, they are treated as all `true` ("compute as much as possible"). -2. **Format forcing**: selecting `.torrent` forces `sha1.whole = true` and `sha1.pieces = true`; selecting `.cas` forces `md5.whole = true` and `md5.pieces = true`. The UI disables these checkboxes with a "required by this format" hint. -3. **Reuse drive hashes**: the generation wizard calls `capabilities` first and pre-selects the hashes the drive already exposes, avoiding redundant downloads. - -The matrix decides whether generation must download: piece hashes are generally not exposed by drive listings, so any `pieces` requirement forces a streaming download; a missing whole hash also forces a download. -::::: - -## 编解码与转换细节 { lang="zh-CN" } - -## Encoding and conversion details { lang="en" } - -::::: zh-CN +## 编解码与转换细节 ### bencode(`.torrent` 的底层编码) @@ -350,62 +184,8 @@ BT v1 中所有文件共享同一个分片序列,因此当 `files.length > 1` ### 格式自动探测 `parse` 接口按首字符自动识别格式:`{` → `.oss`(JSON)、`d` → `.torrent`(bencode 字典)、其余 → `.cas`。也可用显式 `format` 提示强制指定。 -::::: - -::::: en - -### bencode (the encoding underneath `.torrent`) - -`.torrent` uses bencode. Dictionary keys must be sorted by byte order. The encoder handles the four value types: - -- integer → `ie` (errors when out of safe-integer range) -- byte string → `:` -- list → `le` -- dictionary → `de` - -The parser enforces depth ≤ 32, items ≤ 100,000, and rejects trailing data. - -### The `.torrent` info dictionary - -- Single file: `info` holds `name`, `piece length`, `pieces`, `length`, optional `md5sum`. -- Multi-file: `info` holds `name`, `piece length`, `pieces`, `files` (each with `length`, `path`, optional `md5sum`). -- `pieces` is the concatenation of all SHA-1 piece hashes in order (20 bytes each); parsing verifies the piece count equals `ceil(total_size / piece_size)`. -- `info_hash` = SHA-1(bencode(info)), used for magnet links and BT identification. - -OpenList extension keys: -- `x-openlist`: a lossless bencode-embedding of the full `.oss` seed (JSON values recursively mapped to bencode values), letting OpenList clients fully reconstruct the metadata. -- `x-cas`: `{ cloud, file_md5, slice_md5, slice_md5s, slice_size }` for 189pc rapid upload; written only for a single file with MD5. -- `announce` / `announce-list`: derived from `trackers` (first tracker becomes `announce`). -- `creation date` / `comment` / `created by`: standard metadata. - -When `x-openlist` is present at parse time, its consistency with the info dict is validated (name, piece_size, file count, each file's path and size); any mismatch errors out to prevent forgery. - -### Multi-file `.torrent` boundary limit - -All files in a BT v1 torrent share one piece sequence, so when `files.length > 1`, every file except the last must have a size divisible by `piece_size`; otherwise a piece is split by a file boundary and cannot be legally divided. Conversion reports `a file boundary splits a piece`. - -### `.cas` `sliceMd5` rules - -- When the piece size equals 10 MiB (`DEFAULT_PIECE_SIZE`) and a per-piece MD5 list exists: - - one piece → `sliceMd5 = that piece MD5`. - - multiple pieces → `sliceMd5 = md5(per-piece MD5s uppercased and joined by newline)`. -- When per-piece MD5 is absent: - - file ≤ 10 MiB → use the whole-file MD5 as `sliceMd5`. - - file > 10 MiB and no legacy `cas_slice_md5` → conversion errors (cannot derive safely). - -Encoding `.cas` emits base64-encoded JSON; decoding tries base64 first and falls back to plain JSON (for legacy files). - -### Format auto-detection - -The `parse` API detects the format by the first character: `{` → `.oss` (JSON), `d` → `.torrent` (bencode dict), otherwise → `.cas`. An explicit `format` hint can override this. -::::: - -## 架构与实现 { lang="zh-CN" } - -## Architecture { lang="en" } - -::::: zh-CN +## 架构与实现 种子能力由**两套等价实现**提供,分别对应两套后端: @@ -432,42 +212,8 @@ graph LR - **规范化层**:`normalizeSeed` / Go 侧等价函数,统一字段补全与安全校验。 - **转换层**:`.oss` ↔ `.torrent` ↔ `.cas` 的相互编码/解码,含缺失信息诊断。 - **能力预检**:`capabilities` 汇总每个文件「已提供的哈希 / 是否需要下载 / 是否可流式下载」,供前端与生成逻辑复用。 - ::::: - -::::: en - -Seed capability is provided by two equivalent implementations, one per backend: - -| Backend | Language | Format library | API layer | -| ----------------- | ---------- | ------------------------------------------------------------------------- | --------------------------- | -| OpenList-Backends | Go | `pkg/torrent/` (bencode, hash writer, generate, parse, convert, diagnose) | `server/handles/torrent.go` | -| OpenList-TSWorker | TypeScript | `internal/seed/` (`codec.ts`, `hash.ts`, `types.ts`) | `server/seed.ts` | - -Both share the same field spec and codec logic (bencode sorting, CAS `sliceMd5` derivation, hash-matrix normalization, consistency validation), so seeds produced by the Cloudflare Worker and Go versions interoperate. - -```mermaid -graph LR - UI[HopeUI frontend] -->|POST /fs/seed/*| GO[Go backend] - UI -->|POST /fs/seed/*| TS[TS Worker backend] - GO --> FMT[pkg/torrent format library] - TS --> TSFMT[internal/seed codec] - GO --> DRV[drivers: 189pc / PutURL / offline] - TS --> TSDRV[drivers: rapid / session / stream] -``` - -Core components: - -- **`HashWriter` / `TorrentPieceHasher`**: streaming hashers that maintain whole-file hashes (md5/sha1/sha256) plus per-piece hashes in a single pass, avoiding a second read. -- **Normalization layer**: `normalizeSeed` (and the Go equivalent) applies field defaults and safety validation. -- **Conversion layer**: encode/decode between `.oss` ↔ `.torrent` ↔ `.cas`, with missing-information diagnostics. -- **Capability preflight**: `capabilities` summarizes per file "hashes available / download needed / streamable", reused by the UI and generation logic. - ::::: - -## 数据模型 { lang="zh-CN" } - -## Data model { lang="en" } -::::: zh-CN +## 数据模型 种子系统在 Go 与 TS 两端共享同一套数据模型,字段命名保持一致,保证两种后端产出的种子可互操作。核心结构体如下: @@ -539,78 +285,3 @@ Core components: | `files` | `CASFile[]` | 多文件时的文件数组(可选扩展) | > 单文件用顶层五字段(`name`/`size`/`md5`/`sliceMd5`/`create_time`),与参考项目字节级兼容;多文件用 `files` 数组;`slice_md5s`/`slice_size` 保存逐片 MD5,供天翼云秒传复用。 -> ::::: - -::::: en - -Both the Go and TS backends share one data model with identical field names, ensuring seeds from either backend interoperate. The core structures are: - -### `Seed` - -| Field | Type | Description | -| ------------ | ------------------- | ----------------------------------- | -| `format` | `string` | fixed `openlist-sharing-seed` | -| `version` | `number` | fixed `1` | -| `name` | `string` | seed name | -| `comment` | `string` | overall comment | -| `created_at` | `string` (ISO 8601) | creation time | -| `created_by` | `string` | creator | -| `piece_size` | `number` | piece size in bytes, default 10 MiB | -| `trackers` | `string[]` | tracker list | -| `channels` | `SeedChannel[]` | channels that saved successfully | -| `files` | `SeedFile[]` | file array | - -### `SeedFile` - -| Field | Type | Description | -| ------------------ | -------------- | -------------------------------- | -| `path` | `string` | relative path | -| `size` | `number` | size in bytes | -| `modified` | `string` | mtime (ISO 8601) | -| `comment` | `string` | per-file comment | -| `hashes` | `SeedHashes` | hash collection | -| `sources` | `SeedSource[]` | public direct / share sources | -| `cas_slice_md5` | `string` | CAS aggregate slice MD5 (legacy) | -| `cas_create_time` | `string` | CAS creation time (legacy) | -| `missing_channels` | `string[]` | drives that failed rapid upload | - -### `SeedHashes` - -| Field | Type | Description | -| -------- | -------- | ---------------------------------------------- | -| `md5` | `string` | whole-file MD5 | -| `sha1` | `string` | whole-file SHA-1 | -| `sha256` | `string` | whole-file SHA-256 | -| `pieces` | `object` | `{ md5[], sha1[], sha256[] }` per-piece arrays | - -### `SeedSource` - -| Field | Type | Description | -| ------------ | -------- | --------------------------------------------------- | -| `type` | `string` | `openlist-direct` or `openlist-share` | -| `url` | `string` | absolute URL, restricted to the configured site URL | -| `expires_at` | `string` | expiry time (optional) | -| `share_id` | `string` | share ID (for `openlist-share`) | - -### `SeedChannel` - -| Field | Type | Description | -| ------------ | -------- | --------------------- | -| `driver` | `string` | driver name | -| `mount_path` | `string` | mount path (optional) | - -### `CASPayload` (`.cas` payload) - -| Field | Type | Description | -| ------------- | ----------- | -------------------------------------------------- | -| `name` | `string` | file name | -| `size` | `number` | file size | -| `md5` | `string` | whole MD5 | -| `sliceMd5` | `string` | aggregate slice MD5 | -| `create_time` | `string` | creation time | -| `slice_md5s` | `string[]` | per-piece MD5 (optional extension) | -| `slice_size` | `number` | piece size (optional extension, default 10 MiB) | -| `files` | `CASFile[]` | per-file array for multi-file (optional extension) | - -> Single file uses the top-level five fields (`name`/`size`/`md5`/`sliceMd5`/`create_time`), byte-compatible with the reference project; multiple files use the `files` array; `slice_md5s`/`slice_size` preserve per-piece MD5 for 189pc rapid upload. -> ::::: diff --git a/pages/seeds/faq.md b/pages/seeds/faq.md index 13fb4ff22..d9789e534 100644 --- a/pages/seeds/faq.md +++ b/pages/seeds/faq.md @@ -1,39 +1,20 @@ --- -title: - en: FAQ - zh-CN: 常见问题 categories: - seeds top: 940 --- -## 注意事项 { lang="zh-CN" } +# 常见问题 -## Notes { lang="en" } - -::::: zh-CN +## 注意事项 - `.cas` 的逐片 MD5 与多文件结构依赖可选扩展字段,单文件五字段与参考项目字节级兼容。 - 中转秒传要求中间存储支持同步原生复用或 PutURL。 - 重算要求源文件已存在于服务端。 - 生成的 `.torrent` 是合法 BT 文件,但 OpenList 本身不作为 BT peer。 - 所有种子解析均受字节数、文件数、深度与路径穿越限制。 - ::::: - -::::: en - -- `.cas` per-piece MD5 and multi-file support rely on optional extension fields; the single-file five fields remain byte-compatible with the reference project. -- Relayed transfer requires an intermediate storage with synchronous native reuse or PutURL. -- Recalculation requires the source file to already exist on the server. -- The generated `.torrent` is a valid BT file, but OpenList itself is not a BT peer. -- All seed parsing is bounded by byte, file-count, depth, and path-traversal limits. - ::::: - -## 常见问题 { lang="zh-CN" } -## Common questions { lang="en" } - -::::: zh-CN +## 常见问题 ### 生成的 `.cas` 被标记为 Legacy CAS 是什么原因? @@ -82,55 +63,3 @@ BT v1 中所有文件共享同一个分片序列。多文件时除最后一个 ### 为什么异步种子生成任务在服务重启后会丢失? 超过 1 GiB 的生成请求会转为后台任务,但异步任务不持久化;服务重启后未完成的任务会丢失,需要重新发起生成。小文件(≤ 1 GiB)同步生成,不受影响。 -::::: - -::::: en - -### Why is my generated `.cas` flagged as Legacy CAS? - -A legacy CAS only carries the five fields `name`, `size`, `md5`, aggregate `sliceMd5`, and creation time, with no per-piece MD5 list, so conversion is lossy. Newer generation writes `slice_md5s` / `slice_size` to preserve per-piece MD5, and the preview no longer shows the Legacy CAS warning when piece hashes are present. - -### Why can't a seed with only MD5 convert to `.torrent`? - -`.torrent` follows the BT v1 spec and requires SHA-1 whole + piece hashes. MD5 alone cannot produce a valid `pieces` field; conversion reports the missing SHA-1 pieces explicitly. - -### Why does multi-file `.torrent` conversion report "a file boundary splits a piece"? - -All files in a BT v1 torrent share one piece sequence. In multi-file mode, every file except the last must have a size divisible by `piece_size`; otherwise a piece is cut by a file boundary and cannot be legally divided. Adjust the piece size or use a single file so sizes align. - -### Why does rapid upload report `unavailable`? - -When the destination drive cannot reuse the hashes in the current seed, the operation reports `unavailable` instead of silently degrading to a normal upload. Make sure the seed contains the hashes the target drive requires (see the [rapid upload matrix](/seeds/rapid-upload)). - -### Why can't relayed transfer use asynchronous offline download? - -Asynchronous offline download cannot guarantee the intermediate storage has fully landed before the server-side copy to the final drive. Relaying therefore requires the intermediate storage to support synchronous native reuse or PutURL. - -### Can a generated `.torrent` be downloaded by a generic BT client? - -The generated `.torrent` is a valid BT v1 file and can be handed to an offline tool via magnet. However, OpenList itself is not a BT peer; whether generic BT clients can download depends on the tracker/webseed deployment. - -### Why does recalculation require the source file on the server? - -Recalculation re-reads an existing server-side file to recompute hashes; it never downloads from an external source. Upload or save the file to the server first if it is not already there. - -### How do I enable automatic sidecar generation on upload? - -The upload API is controlled by headers: set `X-Seed-Sidecars` (e.g. `oss,torrent,cas`) to specify formats directly, or combine `X-Generate-Seed: on` with the storage-level `seed_policy` and the global `seed_auto_generate_policy`. The global policy defaults to `off`, so nothing is generated by default. - -### Why can't asynchronous upload (`as_task=true`) generate sidecars? - -Sidecar generation must read the full upload stream and hash it in one pass. An async task cannot guarantee the complete stream is available in the same process and written synchronously, so the upload API returns 400 for the "sidecars + async upload" combination. - -### What does an all-empty hash matrix mean? - -When all six flags (md5/sha1/sha256 × whole/pieces) are `false`, the system treats it as "compute as much as possible", equivalent to selecting everything. To produce a minimal seed, explicitly select only the hashes you need. - -### What is `x-openlist` inside a `.torrent`? - -`x-openlist` is OpenList's lossless extension key that bencode-embeds the full `.oss` seed into a standard torrent. OpenList clients use it to fully reconstruct MD5/SHA256, comments, channels, and shares; parsing also validates its consistency with the info dictionary to prevent forgery. - -### Why are background seed-generation tasks lost on restart? - -Requests over 1 GiB are queued as background tasks, but those tasks are not persisted; an unfinished task is lost when the service restarts and must be re-triggered. Small files (≤ 1 GiB) are generated synchronously and are unaffected. -::::: diff --git a/pages/seeds/generate.md b/pages/seeds/generate.md index 5538cf960..1723c55f4 100644 --- a/pages/seeds/generate.md +++ b/pages/seeds/generate.md @@ -1,29 +1,17 @@ --- -title: - en: Generating seeds - zh-CN: 生成种子 categories: - seeds top: 970 --- -## 上传时生成 { lang="zh-CN" } +# 生成种子 -## Generate on upload { lang="en" } +## 上传时生成 -::::: zh-CN 上传文件时,可勾选生成 `.oss` / `.torrent` / `.cas` 侧车(默认全部关闭)。系统会在上传流式计算哈希时一并生成侧车,无需二次读取。 -::::: -::::: en -While uploading, you can enable `.oss` / `.torrent` / `.cas` sidecars (all off by default). The sidecars are produced alongside the streaming hash calculation without a second read. -::::: +## 右键生成 -## 右键生成 { lang="zh-CN" } - -## Generate from the context menu { lang="en" } - -::::: zh-CN 在文件列表右键(支持多选)选择「生成传输种子」,进入生成向导: 1. **种子名**:可编辑,缺省自动推导(单选用文件名、多选公共前缀或文件夹名)。 @@ -32,24 +20,8 @@ While uploading, you can enable `.oss` / `.torrent` / `.cas` sidecars (all off b 4. **分片大小**:默认 10 MiB,可自定义。 5. **文件列表**:展示每个文件的大小、驱动已提供哈希、是否需下载、生成方式(直接/下载),每文件可填注释、可单独勾选分享/直链。 6. **Tracker**:从系统配置的 tracker 列表中勾选。 - ::::: - -::::: en -Right-click one or more files and choose "Generate transfer seed" to open the wizard: - -1. **Seed name**: editable, auto-derived when empty (file name for single selection, common prefix or folder for multiple). -2. **Formats**: enable `.oss` / `.torrent` / `.cas` (any combination). -3. **Hash matrix**: choose MD5 / SHA-1 / SHA-256 whole and piece hashes, pre-selecting the hashes the drive already provides. -4. **Piece size**: defaults to 10 MiB. -5. **File list**: shows each file's size, drive-provided hashes, whether a download is needed, and the generation mode (direct/download); per-file comments and per-file share/direct toggles. -6. **Trackers**: picked from the system-configured tracker list. - ::::: - -## 种子命名 { lang="zh-CN" } -## Seed naming { lang="en" } - -::::: zh-CN +## 种子命名 种子名可编辑,缺省时按以下优先级自动推导: @@ -59,43 +31,14 @@ Right-click one or more files and choose "Generate transfer seed" to open the wi 4. **兜底**:`OpenList Seed`。 命名同样受安全检查约束(拒绝空名、`.`、`..`、含 `/` 或 `\` 的名称)。 -::::: - -::::: en - -The seed name is editable; when empty it is auto-derived in this priority: - -1. **Single file**: the file name with its extension removed. -2. **Multiple files sharing a base name**: if all files share the same name after removing extensions (e.g. `a.mkv`, `a.srt`, `a.ass` in one folder), use that common name. -3. **Otherwise**: the common parent directory name. -4. **Fallback**: `OpenList Seed`. -Naming is subject to the same safety checks (rejecting empty names, `.`, `..`, or names containing `/` or `\`). -::::: - -## 生成方式 { lang="zh-CN" } - -## Generation modes { lang="en" } - -::::: zh-CN +## 生成方式 - **直接生成**:驱动已提供全部所需哈希,无需下载。 - **下载后生成**:需下载文件边下载边计算,界面显示预计流量。 - 若驱动不支持服务器流式下载,会禁止生成并提示;超过 1 GiB 的请求会自动转为后台任务异步生成。 - ::::: - -::::: en - -- **Direct**: the drive already provides all required hashes, no download needed. -- **Download**: files are streamed and hashed on the fly, with estimated traffic shown. -- If the drive cannot stream files, generation is blocked with a hint; requests over 1 GiB are queued as a background task. - ::::: -## 生成接口 { lang="zh-CN" } - -## Generate API { lang="en" } - -::::: zh-CN +## 生成接口 右键生成调用 `POST /fs/seed/generate`(兼容旧接口 `POST /fs/torrent/generate`),请求体字段: @@ -114,34 +57,8 @@ Naming is subject to the same safety checks (rejecting empty names, `.`, `..`, o | `output_path` | `string` | 生成侧车的保存目录 | 返回 `SeedGenerateResult`:同步完成时包含产物路径;`task` 或 `async` 字段非空表示已转入后台异步任务。 -::::: - -::::: en - -Right-click generation calls `POST /fs/seed/generate` (compatible with the legacy `POST /fs/torrent/generate`). Request body: -| Field | Type | Description | -| --------------- | ---------------------- | ---------------------------------------------------- | -| `paths` | `string[]` | source file/directory paths | -| `formats` | `SeedFormat[]` | target formats: `oss` / `torrent` / `cas` | -| `hash_matrix` | `SeedHashMatrix` | hash matrix (see [Design principles](/seeds/design)) | -| `piece_size` | `number` | piece size in bytes, default 10 MiB | -| `name` | `string` | seed name (optional, auto-derived) | -| `comment` | `string` | overall comment (optional) | -| `file_comments` | `Record` | per-file comments (optional) | -| `trackers` | `string[]` | tracker list (optional) | -| `share_files` | `string[]` | file paths to record share links (`/sd/`) | -| `direct_files` | `string[]` | file paths to record public direct links (`/d/`) | -| `output_path` | `string` | directory to save the generated sidecars | - -Returns `SeedGenerateResult`: on synchronous completion it carries the produced paths; a non-empty `task` or `async` field means the request was queued as a background task. -::::: - -## 上传侧车机制 { lang="zh-CN" } - -## Upload sidecar mechanics { lang="en" } - -::::: zh-CN +## 上传侧车机制 上传接口(`POST /fs/put`)会在上传流中**边传边哈希**,完成后把侧车与主文件一并写入目标目录。是否生成由以下优先级判定: @@ -165,57 +82,15 @@ Returns `SeedGenerateResult`: on synchronous completion it carries the produced - 上传结束后校验 `HashWriter` 实际读到的字节数等于声明大小,不等则报错(保证侧车哈希与落盘文件一致)。 - 侧车按 `<文件名>.<格式>` 命名写入同一目录(如 `movie.mp4.torrent`、`movie.mp4.cas`、`movie.mp4.oss`)。 - 生成格式缺省时,从全局设置 `seed_format_policies` 解析出已启用的格式。 - ::::: - -::::: en - -The upload API (`POST /fs/put`) hashes **while streaming** and then writes the sidecars alongside the main file. Whether to generate is decided in this priority order: - -1. Header `X-Seed-Sidecars`: explicit formats (e.g. `oss,torrent,cas`); non-empty enables generation. -2. Header `X-Generate-Seed`: `on` / `true` / `1` enables, `off` disables, `inherit` or empty falls through. -3. Storage-level `seed_policy`: the target storage's `on` / `off` / `inherit`. -4. Global setting `seed_auto_generate_policy`: defaults to `off`. - -Other sidecar headers: - -| Header | Purpose | -| -------------------- | ------------------------------------------------------------ | -| `X-Seed-Format` | Equivalent to `X-Seed-Sidecars`, specifies formats | -| `X-Seed-Piece-Size` | Piece size in bytes; forced to 10 MiB when `cas` is included | -| `X-Seed-Hash-Matrix` | JSON hash matrix controlling which hashes are written | -Constraints and details: - -- Upload sidecars require **synchronous upload**; an async upload (`as_task=true`) returns 400 (sidecars need a complete streaming hash, which cannot be guaranteed in a background task). -- Hashing reuses the upload stream via `io.TeeReader`, so one read both uploads and hashes — no second read. -- After upload, the byte count read by the `HashWriter` is verified against the declared size; a mismatch errors out (keeping sidecar hashes consistent with the landed file). -- Sidecars are written as `.` in the same directory (e.g. `movie.mp4.torrent`, `movie.mp4.cas`, `movie.mp4.oss`). -- When no format is given, enabled formats are resolved from the global `seed_format_policies`. - ::::: - -## 异步任务 { lang="zh-CN" } - -## Background tasks { lang="en" } - -::::: zh-CN +## 异步任务 当生成请求的文件总大小超过 1 GiB 时,系统会将其转为一个后台任务异步执行,避免阻塞请求线程。任务完成后,产物写入指定的 `output_path`,用户可在任务中心查看进度与结果。 超过 1 GiB 阈值自动转后台;其余情况同步完成后直接返回产物路径。 -::::: - -::::: en - -When the total size of a generation request exceeds 1 GiB, it is queued as a background task so the request thread is not blocked. On completion the sidecars are written to `output_path`, and progress/result can be checked in the task center. - -Requests over the 1 GiB threshold go background automatically; everything else returns the produced paths synchronously. -::::: - -## 预览种子 { lang="zh-CN" } -## Previewing seeds { lang="en" } +## 预览种子 -::::: zh-CN 打开 `.oss` / `.torrent` / `.cas` 文件会进入统一预览,展示: - 文件列表与大小 @@ -224,15 +99,3 @@ Requests over the 1 GiB threshold go background automatically; everything else r - Tracker、渠道(channels)、分享/直链(sources) - 格式转换可行性(✓/✗,悬停或点击查看缺失原因) - 可执行的操作(秒传、离线下载、中转、转换、编辑、重算、预览/删除单个文件) - ::::: - -::::: en -Opening a `.oss` / `.torrent` / `.cas` file shows a unified preview with: - -- file list and sizes -- per-file hashes (whole + pieces, copyable with a per-piece popup) -- file comments, timestamps, multi-line overall comment -- trackers, channels, and share/direct sources -- conversion feasibility (✓/✗, hover or click for missing reasons) -- available operations (rapid upload, offline download, transfer, convert, edit, recalculate, preview/remove a single file) - ::::: diff --git a/pages/seeds/index.md b/pages/seeds/index.md index d78811db6..e1e515ecf 100644 --- a/pages/seeds/index.md +++ b/pages/seeds/index.md @@ -1,17 +1,13 @@ --- -title: - en: Transfer Seeds - zh-CN: 传输种子 categories: - seeds top: 1000 --- -## 什么是传输种子 { lang="zh-CN" } +# 传输种子 -## What are Transfer Seeds { lang="en" } +## 什么是传输种子 -:::: zh-CN 传输种子是一套可移植的文件元数据系统,围绕三种侧车(sidecar)格式构建,用于跨网盘、跨用户、跨设备分享文件信息、离线秒传、备份释放空间。 - **`.oss`(openlist-sharing-seed v1)**:信息最完整的 JSON 格式,所有字段均可缺省。 @@ -19,23 +15,8 @@ top: 1000 - **`.cas`**:与参考项目 [OpenList-CAS](https://github.com/GitYuA/OpenList-CAS) 兼容的内容寻址载荷,用于天翼云秒传。 三者可以相互转换(在信息足够时),并支持生成、预览、秒传、离线下载、中转保存、编辑与重算。 -:::: -:::: en -Transfer Seeds are a portable file-metadata system built on three sidecar formats for sharing file information, offline rapid-upload, backup, and space release across drives, users, and devices. - -- **`.oss`** (`openlist-sharing-seed` v1): the most complete JSON format; every field is optional. -- **`.torrent`**: a standard BitTorrent v1 file with OpenList extensions. -- **`.cas`**: a content-addressable payload compatible with the reference project [OpenList-CAS](https://github.com/GitYuA/OpenList-CAS), used for 189pc rapid upload. - -The three formats convert between each other (when enough information is present) and support generation, preview, rapid upload, offline download, relayed save, editing, and recalculation. -:::: - -## 章节导航 { lang="zh-CN" } - -## Sections { lang="en" } - -:::: zh-CN +## 章节导航 - [需求背景](/seeds/background) — 为什么需要传输种子,以及现有能力边界 - [设计理念](/seeds/design) — 设计目标与三种格式的设计结构 @@ -43,14 +24,3 @@ The three formats convert between each other (when enough information is present - [使用种子](/seeds/usage) — 秒传、离线下载、中转、转换、编辑、重算 - [秒传矩阵](/seeds/rapid-upload) — 各驱动提供的哈希与秒传所需哈希 - [常见问题](/seeds/faq) — 注意事项与常见疑问 - :::: - -:::: en - -- [Background](/seeds/background) — why transfer seeds exist and current capability boundaries -- [Design principles](/seeds/design) — design goals and the structure of the three formats -- [Generating seeds](/seeds/generate) — generate on upload / from the context menu / preview -- [Using seeds](/seeds/usage) — rapid upload, offline download, relay, convert, edit, recalculate -- [Rapid upload matrix](/seeds/rapid-upload) — hashes each drive exposes and requires -- [FAQ](/seeds/faq) — notes and common questions - :::: diff --git a/pages/seeds/rapid-upload.md b/pages/seeds/rapid-upload.md index f34906112..e92117580 100644 --- a/pages/seeds/rapid-upload.md +++ b/pages/seeds/rapid-upload.md @@ -1,73 +1,35 @@ --- -title: - en: Rapid upload matrix - zh-CN: 秒传矩阵 categories: - seeds top: 950 --- -:::::: zh-CN -种子系统依赖网盘提供的哈希来避免重复下载。不同网盘「列表提供的哈希」与「秒传所需的哈希」各不相同。下面把这两个维度合并为一张二维矩阵:**行 = 源驱动列表提供的哈希,列 = 目标驱动秒传所需的哈希**,交叉格即「从源到目标能否秒传」。 -:::::: - -:::::: en -The seed system relies on drive-provided hashes to avoid redundant downloads. Different drives differ in the hashes they "provide in listing" and "require for rapid upload". The two dimensions are merged below into a single two-dimensional matrix: **rows = hashes provided by the source listing, columns = hashes required by the target for rapid upload**; each cell answers "can this source rapid-upload to this target". -:::::: - -## 图例 { lang="zh-CN" } - -## Legend { lang="en" } - -:::::: zh-CN -| 符号 | 含义 | -|---|---| -| ✅ | 可直接秒传:源列表已提供目标所需的全部哈希,无需下载文件内容 | -| 🔶 | 需补算哈希:源提供了部分所需哈希,但缺少分片哈希或其它算法,需下载文件补算后才能秒传 | -| ❌ | 无法秒传:源缺少目标秒传所需的关键哈希 | -| ➖ | 不适用:目标驱动本身不支持秒传(只能普通上传) | -:::::: - -:::::: en -| Symbol | Meaning | -|---|---| -| ✅ | Direct rapid upload: the source listing already provides every hash the target needs, no content download | -| 🔶 | Needs hashing: the source provides part of the required hashes but is missing piece hashes or another algorithm, so the file must be downloaded to compute the rest first | -| ❌ | Cannot rapid upload: the source lacks the key hash the target requires | -| ➖ | Not applicable: the target drive does not support rapid upload (normal upload only) | -:::::: +# 秒传矩阵 -## 秒传矩阵 { lang="zh-CN" } +种子系统依赖网盘提供的哈希来避免重复下载。不同网盘「列表提供的哈希」与「秒传所需的哈希」各不相同。下面把这两个维度合并为一张二维矩阵:**行 = 源驱动列表提供的哈希,列 = 目标驱动秒传所需的哈希**,交叉格即「从源到目标能否秒传」。 -## Rapid upload matrix { lang="en" } +## 图例 -:::::: zh-CN -| 源提供 \ 目标需要 | MD5 | MD5 + 分片 | SHA1 | MD5 + SHA1 | SHA1 或 MD5 | GCID | SHA256 | 无秒传 | -|---|---|---|---|---|---|---|---|---| -| **MD5** | ✅ | 🔶 | ❌ | 🔶 | ✅ | ❌ | ❌ | ➖ | -| **SHA1** | ❌ | ❌ | ✅ | 🔶 | ✅ | ❌ | ❌ | ➖ | -| **MD5 + SHA1** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ❌ | ➖ | -| **MD5 + SHA1 + SHA256** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ✅ | ➖ | -| **GCID**(分块 SHA1) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ➖ | -| **无** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ➖ | -:::::: +| 符号 | 含义 | +| ---- | ------------------------------------------------------------------------------------ | +| ✅ | 可直接秒传:源列表已提供目标所需的全部哈希,无需下载文件内容 | +| 🔶 | 需补算哈希:源提供了部分所需哈希,但缺少分片哈希或其它算法,需下载文件补算后才能秒传 | +| ❌ | 无法秒传:源缺少目标秒传所需的关键哈希 | +| ➖ | 不适用:目标驱动本身不支持秒传(只能普通上传) | -:::::: en -| Source provides \ Target needs | MD5 | MD5 + pieces | SHA1 | MD5 + SHA1 | SHA1 or MD5 | GCID | SHA256 | no rapid upload | -|---|---|---|---|---|---|---|---|---| -| **MD5** | ✅ | 🔶 | ❌ | 🔶 | ✅ | ❌ | ❌ | ➖ | -| **SHA1** | ❌ | ❌ | ✅ | 🔶 | ✅ | ❌ | ❌ | ➖ | -| **MD5 + SHA1** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ❌ | ➖ | -| **MD5 + SHA1 + SHA256** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ✅ | ➖ | -| **GCID** (block SHA1) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ➖ | -| **none** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ➖ | -:::::: +## 秒传矩阵 -## 驱动哈希能力映射 { lang="zh-CN" } +| 源提供 \ 目标需要 | MD5 | MD5 + 分片 | SHA1 | MD5 + SHA1 | SHA1 或 MD5 | GCID | SHA256 | 无秒传 | +| ----------------------- | --- | ---------- | ---- | ---------- | ----------- | ---- | ------ | ------ | +| **MD5** | ✅ | 🔶 | ❌ | 🔶 | ✅ | ❌ | ❌ | ➖ | +| **SHA1** | ❌ | ❌ | ✅ | 🔶 | ✅ | ❌ | ❌ | ➖ | +| **MD5 + SHA1** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ❌ | ➖ | +| **MD5 + SHA1 + SHA256** | ✅ | 🔶 | ✅ | ✅ | ✅ | ❌ | ✅ | ➖ | +| **GCID**(分块 SHA1) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ➖ | +| **无** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ➖ | -## Drive hash capability mapping { lang="en" } +## 驱动哈希能力映射 -:::::: zh-CN 下表把每个驱动映射到矩阵的行(列表提供)与列(秒传所需),据此即可在矩阵中定位任意「源 → 目标」组合。 | 驱动 | 列表提供哈希(源 / 行) | 秒传所需哈希(目标 / 列) | @@ -93,86 +55,21 @@ The seed system relies on drive-provided hashes to avoid redundant downloads. Di | Google Drive | MD5 + SHA1 + SHA256 | 无秒传(普通上传) | | OneDrive | 无 | 无秒传(普通上传) | -:::::: - -:::::: en -The table maps each drive to the matrix's rows (listing provides) and columns (rapid upload needs), so any "source → target" pair can be located in the matrix. - -| Drive | Hashes provided (source / row) | Rapid upload requires (target / column) | -| ------------------ | -------------------------------- | --------------------------------------- | -| 189pc | MD5 | MD5 + piece MD5 (`slice_md5`) | -| 189 (legacy) | MD5 | MD5 + piece MD5 | -| 189tv | MD5 | MD5 | -| Aliyundrive | SHA1 | SHA1 (`pre_hash` + `content_hash`) | -| Aliyundrive Open | SHA1 | SHA1 (`pre_hash`) | -| Baidu | none (returned MD5 is untrusted) | MD5 (`content-md5` + `slice-md5`) | -| PikPak | GCID (block SHA1) | GCID | -| 115 | SHA1 | SHA1 | -| 115 Open | SHA1 | SHA1 | -| Thunder | GCID | GCID | -| Thunder X | GCID | GCID | -| Thunder Browser | GCID | GCID | -| Quark Open | SHA1 | MD5 + SHA1 | -| Quark UC | none | MD5 + SHA1 | -| febbox | GCID | GCID | -| 139 (China Mobile) | none (`digest` not exposed) | SHA256 (rapid upload) | -| 123 | MD5 (`etag`) | MD5 | -| 123 Open | SHA1 / MD5 | SHA1 (`sha1_reuse`) or MD5 | -| Google Drive | MD5 + SHA1 + SHA256 | no rapid upload (normal upload) | -| OneDrive | none | no rapid upload (normal upload) | - -:::::: - -## 如何阅读矩阵 { lang="zh-CN" } - -## How to read the matrix { lang="en" } - -:::::: zh-CN +## 如何阅读矩阵 - **阿里云盘 → 115**:源提供 SHA1,目标需 SHA1 → 矩阵 `SHA1 × SHA1` = ✅ 可直接秒传。 - **123 网盘 → 天翼云 189pc**:源提供 MD5,目标需 MD5 + 分片 → 矩阵 `MD5 × MD5+分片` = 🔶 需下载补算分片 MD5。 - **PikPak → 阿里云盘**:源提供 GCID,目标需 SHA1 → 矩阵 `GCID × SHA1` = ❌ 无法秒传。 - **任意源 → Google Drive / OneDrive**:目标无秒传 → 矩阵末列 = ➖,只能普通上传。 - :::::: - -:::::: en - -- **Aliyundrive → 115**: source provides SHA1, target needs SHA1 → matrix `SHA1 × SHA1` = ✅ direct rapid upload. -- **123 → 189pc**: source provides MD5, target needs MD5 + pieces → matrix `MD5 × MD5+pieces` = 🔶 must download to compute piece MD5. -- **PikPak → Aliyundrive**: source provides GCID, target needs SHA1 → matrix `GCID × SHA1` = ❌ cannot rapid upload. -- **Any source → Google Drive / OneDrive**: target has no rapid upload → last column = ➖, normal upload only. - :::::: - -## 与种子矩阵的关系 { lang="zh-CN" } -## Relation to the hash matrix { lang="en" } +## 与种子矩阵的关系 -:::::: zh-CN 生成种子时,内容矩阵会依据上述能力预检:若源驱动已提供目标所需全部哈希,则无需下载文件即可生成;否则会下载后边下载边计算。勾选 `.torrent` 会强制计算 SHA-1 完整 + 分片;勾选 `.cas` 会强制计算 MD5 完整 + 分片(固定 10 MiB),以满足天翼云秒传。 -:::::: -:::::: en -The hash matrix is preflighted against these capabilities: if the source drive already provides every hash the target needs, no download is required; otherwise files are streamed and hashed on the fly. Selecting `.torrent` forces SHA-1 whole + pieces; selecting `.cas` forces MD5 whole + pieces (fixed 10 MiB) for 189pc rapid upload. -:::::: - -## 说明 { lang="zh-CN" } - -## Notes { lang="en" } - -:::::: zh-CN +## 说明 - **GCID** 为分块 SHA1(40 位),用于 PikPak / 迅雷族 / febbox。 - 天翼云 189 与百度网盘的分片上传另需逐片 MD5(`slice_md5`),因此矩阵中对应列为 `MD5 + 分片`,源仅提供 MD5 时记为 🔶(需下载补算分片)。 - 阿里云盘族使用 `pre_hash`(文件头 1024 字节 SHA1)触发秒传,命中后补算完整 SHA1。 - 移动云盘 139 快传使用 SHA256;123 Open 支持 SHA1 复用(`sha1_reuse`)或 MD5 任选其一。 - **注意**:矩阵反映各驱动的原生秒传能力。当前种子「秒传保存」流程仅天翼云 189pc 的 CAS 秒传被完整接入,其余驱动的秒传在普通上传(`Put` / `PutRapid`)路径中生效。 - :::::: - -:::::: en - -- **GCID** is a block SHA1 (40 hex chars), used by PikPak / Thunder family / febbox. -- 189 and Baidu chunked uploads additionally need per-piece MD5 (`slice_md5`), so their column is `MD5 + pieces`; a source providing only MD5 is marked 🔶 (must download to compute pieces). -- The Aliyundrive family uses `pre_hash` (SHA1 of the first 1024 bytes) to trigger rapid upload, then computes the full SHA1 on hit. -- 139 rapid upload uses SHA256; 123 Open supports either SHA1 reuse (`sha1_reuse`) or MD5. -- **Note**: the matrix reflects each drive's native rapid-upload capability. Currently only 189pc CAS rapid upload is wired into the seed "rapid save" flow; other drives' rapid upload takes effect in the normal upload path (`Put` / `PutRapid`). - :::::: diff --git a/pages/seeds/usage.md b/pages/seeds/usage.md index 12876ae21..9f15bfb15 100644 --- a/pages/seeds/usage.md +++ b/pages/seeds/usage.md @@ -1,17 +1,12 @@ --- -title: - en: Using seeds - zh-CN: 使用种子 categories: - seeds top: 960 --- -## 种子接口总览 { lang="zh-CN" } +# 使用种子 -## Seed API overview { lang="en" } - -::::: zh-CN +## 种子接口总览 种子能力暴露在 `POST /fs/seed/*` 下(旧 `/fs/torrent/*` 路由仍兼容): @@ -42,46 +37,8 @@ top: 960 | `recalc_files` | 待重算的文件(`path` + `source_path`) | | `hash_matrix` | 重算用的内容矩阵 | -::::: - -::::: en - -Seed capability is exposed under `POST /fs/seed/*` (the legacy `/fs/torrent/*` routes remain compatible): - -| API | Purpose | -| ------------------ | ------------------------------------------------------------------------------------------------------------------ | -| `parse` | Parse a seed (auto-detects `.oss` / `.torrent` / `.cas`) | -| `upload_parse` | Upload a seed file and parse it | -| `generate` | Generate a seed (see [Generating seeds](/seeds/generate)) | -| `convert` | Convert between formats | -| `diagnose` | Diagnose missing information for conversion | -| `capabilities` | Preflight: per-file hashes available / download needed / streamable; or probe a destination storage's save methods | -| `rapid_upload` | Rapid-upload save | -| `offline_download` | Offline download / relayed transfer | -| `update` | Edit / recalculate / remove files | -| `update_channels` | Update channels (write `channels` / `missing_channels` on success/failure) | -| `quick_save` | Quick save (alias of `rapid_upload` / `offline_download`) | - -All seed payloads are base64-encoded in the request body. Common `SeedOperationRequest` fields: +## 秒传保存 -| Field | Description | -| ---------------------- | ------------------------------------------------------------------------------- | -| `seed_data` | base64 seed content (aliases `content` / `data` / `torrent_data` also accepted) | -| `file_name` | original seed file name | -| `path` / `target_path` | destination directory | -| `selected_files` | indices into the seed's `files[]` | -| `update_channel` | whether to update channels on success | -| `remove_files` | file paths to remove | -| `recalc_files` | files to recalculate (`path` + `source_path`) | -| `hash_matrix` | hash matrix for recalculation | - -::::: - -## 秒传保存 { lang="zh-CN" } - -## Rapid upload { lang="en" } - -::::: zh-CN 勾选文件后选择目标网盘和路径,界面会展示目标驱动支持的秒传方式(如天翼云 CAS)以及当前是否可秒传。系统优先使用原生秒传,若目标驱动无法复用哈希,会明确提示 `unavailable`,不会静默降级。 秒传保存的逐文件判定(`capabilities` 返回的 `method`): @@ -95,119 +52,42 @@ All seed payloads are base64-encoded in the request body. Common `SeedOperationR | `unavailable` | 目标驱动不支持任何可用方式 | 红 | 只有 `189pc_cas` 与 `put_url` 属于真正的「秒传」,无需下载文件内容。 -::::: - -::::: en -Select files and choose a destination drive/path. The UI shows the destination drive's supported rapid-upload methods (e.g. 189pc CAS) and whether rapid upload is currently possible. Native rapid upload is preferred; if the drive cannot reuse hashes, the operation reports `unavailable` instead of silently degrading. -Per-file save decision (`method` returned by `capabilities`): +## 离线下载 -| Method | Meaning | Badge | -| ------------------- | ------------------------------------------------------------------------------------------ | ------ | -| `189pc_cas` | destination is 189pc and the seed has the MD5 + piece MD5 CAS needs, so rapid upload works | green | -| `put_url` | the seed has a usable direct link, so PutURL pulls server-side | green | -| `offline_download` | falls back to an offline download tool | blue | -| `download_required` | hashes cannot be reused; the server streams and re-uploads | yellow | -| `unavailable` | the destination drive supports no usable method | red | - -Only `189pc_cas` and `put_url` are true "rapid upload" (no content download). -::::: - -## 离线下载 { lang="zh-CN" } - -## Offline download { lang="en" } - -::::: zh-CN 当种子内含可用直链/分享链接时,可将文件下载到目标网盘(PutURL / 离线工具 / 服务器流式)。BT 种子可通过 magnet 交给离线工具下载。 - `.torrent` 种子:整体转 magnet 后交给离线工具(`announce` → `tr`、`name` → `dn`、`length` → `xl`)。 - 其它种子:逐文件按 `sources`(`/d/` 直链或 `/sd/` 分享链接)离线下载。 - ::::: - -::::: en -When the seed contains usable direct/share sources, files can be downloaded into the destination drive (PutURL / offline tool / server streaming). Torrent seeds can be handed to an offline tool via magnet. -- `.torrent`: converted to magnet as a whole (`announce` → `tr`, `name` → `dn`, `length` → `xl`) and handed to the offline tool. -- Other seeds: downloaded per file from `sources` (`/d/` direct or `/sd/` share links). - ::::: +## 中转秒传 -## 中转秒传 { lang="zh-CN" } - -## Relayed transfer { lang="en" } - -::::: zh-CN 先选择一个中间网盘(支持原生秒传或 PutURL),保存后再服务器端复制到最终目标网盘。异步离线下载不可用于中转。 请求中设置 `transit_path`(中间路径)并携带 `options: { mode: "transfer" }`,系统会先同步保存到中间存储,再复制到最终 `path`。 -::::: - -::::: en -Save synchronously into an intermediate drive (native rapid upload or PutURL), then copy server-side to the final drive. Asynchronous offline downloads cannot be relayed. - -Set `transit_path` (the intermediate path) with `options: { mode: "transfer" }`; the system saves synchronously to the intermediate storage, then copies to the final `path`. -::::: -## 成功后更新渠道 { lang="zh-CN" } +## 成功后更新渠道 -## Update channel on success { lang="en" } - -::::: zh-CN 勾选「成功后更新渠道」后,秒传成功会把当前网盘追加到种子的 `channels`,失败则记录到 `missing_channels`,方便下次选择可秒传的驱动。 - `channels`:`{ driver, mount_path }` 数组,记录种子已成功落盘的渠道。 - `missing_channels`:秒传失败的驱动名数组,避免下次重复尝试。 - ::::: - -::::: en -With "update channel on success" enabled, a successful save appends the current drive to the seed's `channels`, while a failure is recorded in `missing_channels`. - -- `channels`: an array of `{ driver, mount_path }` marking drives where the seed has landed successfully. -- `missing_channels`: an array of drive names that failed rapid upload, avoiding repeat attempts. - ::::: -## 格式转换 { lang="zh-CN" } +## 格式转换 -## Convert { lang="en" } - -::::: zh-CN 可将种子转换为其他格式。转换前会展示缺失信息,例如缺少 SHA-1 分片时无法转为 `.torrent`。 - 目标格式由 `format` / `to_format` / `target_format` 任一字段指定。 - `diagnose` 接口可单独列出转换每个目标格式所需但缺失的哈希,前端据此显示 ✓/✗ 与缺失原因。 - 转换的可行性规则与生成一致:`.torrent` 需 SHA-1 完整 + 分片;`.cas` 需 MD5 完整 + 分片(或 ≤ 10 MiB 的整文件 MD5)。 - ::::: - -::::: en -Convert between formats; missing information (e.g. no SHA-1 pieces) is reported before conversion. -- The target format is given by `format` / `to_format` / `target_format`. -- `diagnose` lists the hashes required but missing for each target format, driving the ✓/✗ UI and its reasons. -- Feasibility mirrors generation: `.torrent` needs SHA-1 whole + pieces; `.cas` needs MD5 whole + pieces (or a whole-file MD5 for files ≤ 10 MiB). - ::::: - -## 编辑与重算 { lang="zh-CN" } - -## Edit and recalculate { lang="en" } - -::::: zh-CN +## 编辑与重算 - **编辑**:修改整体注释、tracker、渠道、每文件注释、分享直链;编辑时会校验分享链接是否仍有效(无效分享会明确提示其 ID)。 - **重算**:选择源目录后重算哈希(可勾选哈希矩阵)。源文件路径 = 源目录 + 种子内相对路径;重算只读服务端已有文件,不从外部下载。 - **删除文件**:通过 `remove_files` 从种子中移除指定文件。 - ::::: - -::::: en - -- **Edit**: update overall comment, trackers, channels, per-file comments, and share sources; share validity is checked during editing (invalid shares report their IDs). -- **Recalculate**: pick a source directory and recompute hashes (with a selectable hash matrix). The source path is the source directory plus the seed's relative path; recalculation only reads existing server-side files. -- **Remove files**: drop specified files from the seed via `remove_files`. - ::::: -## 文件操作侧车跟随 { lang="zh-CN" } - -## File-operation sidecar follow { lang="en" } - -::::: zh-CN +## 文件操作侧车跟随 对主文件执行复制 / 移动 / 重命名 / 删除时,可勾选「跟随传输种子侧车」,让伴随的 `.oss` / `.torrent` / `.cas` / `.cas.torrent` 一起操作,保持主文件与侧车一致。 @@ -220,51 +100,17 @@ Convert between formats; missing information (e.g. no SHA-1 pieces) is reported - 侧车路径规则:主文件路径 + `.oss` / `.torrent` / `.cas` / `.cas.torrent`。 - 仅对文件生效(目录不跟随);仅处理实际存在的侧车,缺失时静默跳过。 - ::::: - -::::: en - -When copying / moving / renaming / deleting a main file, enable "Follow transfer seed sidecars" to move the companion `.oss` / `.torrent` / `.cas` / `.cas.torrent` along, keeping the main file and sidecars consistent. - -| Operation | API | `follow_seed` behavior | -| --------- | ----------------- | ---------------------------------------------- | -| Copy | `POST /fs/copy` | synchronously copy sidecars to the destination | -| Move | `POST /fs/move` | synchronously move sidecars to the destination | -| Rename | `POST /fs/rename` | rename sidecars to `newName + original suffix` | -| Remove | `POST /fs/remove` | synchronously delete sidecars | - -- Sidecar path rule: main path + `.oss` / `.torrent` / `.cas` / `.cas.torrent`. -- Files only (directories are skipped); missing sidecars are silently ignored. - ::::: - -## 设置 { lang="zh-CN" } - -## Settings { lang="en" } - -::::: zh-CN -| 设置 | 说明 | 默认 | -|---|---|---| -| `seed_site_url` | 生成分享/直链使用的公开站点 URL | 空 | -| `seed_default_matrix` | 右键生成的默认内容矩阵 | md5/sha1/sha256 whole=true | -| `seed_default_trackers` | 生成时可选勾选的 tracker 列表 | 空 | -| `seed_default_format` | 默认种子格式 | oss | -| `seed_format_policies` | 各格式自动生成开关 | 全关 | -| `seed_auto_generate_policy` | 全局自动生成策略 | off | -| `seed_single_direct_preview` | 单文件种子直接预览 | false | -| `seed_cas_direct_access` | 打开 CAS 立即秒传+预览 | false | -| 每存储 `seed_policy` | 存储级 `inherit`/`on`/`off` 覆盖 | inherit | -::::: -::::: en -| Setting | Description | Default | -|---|---|---| -| `seed_site_url` | Public site URL for share/direct sources | empty | -| `seed_default_matrix` | Default right-click hash matrix | md5/sha1/sha256 whole=true | -| `seed_default_trackers` | Tracker list offered at generation | empty | -| `seed_default_format` | Default seed format | oss | -| `seed_format_policies` | Per-format auto-generation switches | all off | -| `seed_auto_generate_policy` | Global auto-generation policy | off | -| `seed_single_direct_preview` | Direct preview for single-file seeds | false | -| `seed_cas_direct_access` | Rapid-upload and preview CAS on open | false | -| per-storage `seed_policy` | Storage-level `inherit`/`on`/`off` override | inherit | -::::: +## 设置 + +| 设置 | 说明 | 默认 | +| ---------------------------- | -------------------------------- | -------------------------- | +| `seed_site_url` | 生成分享/直链使用的公开站点 URL | 空 | +| `seed_default_matrix` | 右键生成的默认内容矩阵 | md5/sha1/sha256 whole=true | +| `seed_default_trackers` | 生成时可选勾选的 tracker 列表 | 空 | +| `seed_default_format` | 默认种子格式 | oss | +| `seed_format_policies` | 各格式自动生成开关 | 全关 | +| `seed_auto_generate_policy` | 全局自动生成策略 | off | +| `seed_single_direct_preview` | 单文件种子直接预览 | false | +| `seed_cas_direct_access` | 打开 CAS 立即秒传+预览 | false | +| 每存储 `seed_policy` | 存储级 `inherit`/`on`/`off` 覆盖 | inherit | diff --git a/pages/snippets/reverse-tip.md b/pages/snippets/reverse-tip.md new file mode 100644 index 000000000..8ad713a64 --- /dev/null +++ b/pages/snippets/reverse-tip.md @@ -0,0 +1,5 @@ +::: danger 请仔细阅读注意事项 + +本接口是基于历史产物的逆向接口,项目组不会主动进行维护,请勿针对此驱动提出有关于任何修复或进行进一步逆向行为需求的issue。 + +::: diff --git a/pages/snippets/tos-tip.md b/pages/snippets/tos-tip.md new file mode 100644 index 000000000..ae29c75c3 --- /dev/null +++ b/pages/snippets/tos-tip.md @@ -0,0 +1,5 @@ +::: danger + +请规范使用帐号,包含但不限于 **`多人共享使用`**、**`图床、软件床`** 和 **`视频外链到视频网站播放`** 等分发服务以及一些没有提到的不规范使用的方式导致帐号被封禁的,后果自负! + +::: diff --git a/pages/terms.md b/pages/terms.md index 1b9202622..a020ba5db 100644 --- a/pages/terms.md +++ b/pages/terms.md @@ -1,31 +1,19 @@ --- -title: - en: Terms of Service - zh-CN: 使用条款 -# This is the icon of the page -nav: false -hide: true -toc: false -layout: statement -top: 1000 -# A page can have multiple categories +sidebar: false +aside: false +outline: false +pageClass: statement-page +comment: false +contributors: false --- -## AGPL 授权声明 { lang="zh-CN" } +# 使用条款 -::: zh-CN -本软件受 [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html) 许可协议保护。您可以自由使用、修改和分发本软件,但必须遵守 AGPL-3.0 的相关条款,包括在分发和提供服务时公开源代码。详情请参阅上述链接。 -::: - -## AGPL License Notice { lang="en" } +## AGPL 授权声明 -::: en -This software is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html). You are free to use, modify, and distribute the software, but must comply with AGPL-3.0 terms, including disclosing source code when distributing or offering as a service. See the link above for details. -::: - -## 使用条款 { lang="zh-CN" } +本软件受 [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html) 许可协议保护。您可以自由使用、修改和分发本软件,但必须遵守 AGPL-3.0 的相关条款,包括在分发和提供服务时公开源代码。详情请参阅上述链接。 -::: zh-CN +## 使用条款 - 本服务仅供合法用途,用户不得利用本服务从事任何违法活动。 - 用户应自行承担因使用本服务而产生的所有风险和责任。 @@ -35,25 +23,7 @@ This software is licensed under the [GNU Affero General Public License v3.0 (AGP - OpenListTeam 保留要求下游项目以相同协议进行分发及对任何违反授权协议的行为追究相关法律责任的权利。 - 如有任何疑问,请随时联系我们。 -::: - -## Terms of Use { lang="en" } - -::: en - -- This service is for lawful use only. Users must not engage in any illegal activity through this service. -- Users assume all risks and responsibilities resulting from the use of this service. -- We reserve the right to modify, suspend, or terminate the service at any time without prior notice. -- The service is provided "as is" without any express or implied warranties of availability, accuracy, or fitness for a particular purpose. -- All downstream projects based on OpenList must strictly comply with the full terms of the AGPL 3.0 license, including proper attribution, maintaining open-source status, and adopting the same license. -- The OpenListTeam reserves the right to require downstream projects to be distributed under the same license and to pursue any violations of the license terms. -- If you have any questions, please contact us. - -::: - -## 免责声明 { #disclaimer lang="zh-CN" } - -::: zh-CN +## 免责声明 {#disclaimer} - OpenList 项目为免费开源软件,基于上游 Alist 项目分叉开发,由 OpenListTeam 负责维护。 - 本软件按“原样”提供,不附带任何明示或暗示的担保,包括但不限于适销性或特定用途的适用性。 @@ -66,23 +36,3 @@ This software is licensed under the [GNU Affero General Public License v3.0 (AGP - 您需自行承担使用本软件的所有风险,包括但不限于账号被封、下载限速等。 - 任何基于 OpenList 产生的分叉、分支或衍生项目,均为相互独立的项目,具备各自的创建者、维护者和独立反馈渠道,OpenListTeam 不提供任何支持。 - 对任何分叉、分支、衍生项目的任何行为、内容及由此引发的任何后果,均由相应负责人独立承担,OpenListTeam 概不承担任何责任。 - -::: - -## DISCLAIMER { lang="en" } - -::: en - -- THE OPENLIST PROJECT IS FREE AND OPEN-SOURCE SOFTWARE, FORKED FROM THE UPSTREAM ALIST PROJECT AND MAINTAINED BY THE OPENLISTTEAM. -- THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -- THIS SOFTWARE IS LICENSED UNDER THE [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html). YOU ARE FREE TO USE, MODIFY, AND DISTRIBUTE THE SOFTWARE, BUT MUST COMPLY WITH AGPL-3.0 TERMS, INCLUDING DISCLOSING SOURCE CODE WHEN DISTRIBUTING OR OFFERING AS A SERVICE. SEE THE LINK ABOVE FOR DETAILS. -- PLEASE COMPLY WITH RELEVANT LAWS AND REGULATIONS WHEN USING THIS SOFTWARE, AND REFRAIN FROM ANY FORM OF ABUSE. -- THIS SOFTWARE IS IMPLEMENTED BASED ON OFFICIAL SDKS OR APIS, WITHOUT MODIFYING, DAMAGING, OR INTERFERING WITH THEIR BEHAVIOR. -- IT ONLY PERFORMS HTTP 302 REDIRECTS OR TRAFFIC FORWARDING, WITHOUT INTERCEPTING, STORING, OR TAMPERING WITH ANY USER DATA. -- THIS PROJECT IS NOT AFFILIATED WITH ANY OFFICIAL PLATFORM OR SERVICE PROVIDER. -- THE MAINTAINERS ARE NOT RESPONSIBLE FOR ANY DIRECT OR INDIRECT LOSSES RESULTING FROM THE USE OR INABILITY TO USE THIS SOFTWARE. -- YOU ASSUME ALL RISKS ASSOCIATED WITH USING THIS SOFTWARE, INCLUDING BUT NOT LIMITED TO ACCOUNT BANS, DOWNLOAD SPEED LIMITS, ETC. -- ANY FORKS, BRANCHES, OR DERIVATIVE PROJECTS BASED ON OPENLIST ARE INDEPENDENT PROJECTS WITH THEIR OWN CREATORS, MAINTAINERS, AND FEEDBACK CHANNELS. THE OPENLISTTEAM DOES NOT PROVIDE ANY SUPPORT FOR THEM. -- ANY ACTIONS, CONTENT, AND CONSEQUENCES ARISING FROM SUCH FORKS, BRANCHES, OR DERIVATIVE PROJECTS ARE THE SOLE RESPONSIBILITY OF THEIR RESPECTIVE MAINTAINERS, AND THE OPENLISTTEAM IS NOT LIABLE FOR ANY SUCH ISSUES. - -::: diff --git a/patches/README.md b/patches/README.md deleted file mode 100644 index 85f9ed707..000000000 --- a/patches/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Patches - -This dir contains patches of node modules. - -## Create - -```bash -pnpm patch package@version -``` - -Edit `./node_modules/.pnpm_patches/package@version`, then commit your changes: - -```bash -pnpm patch-commit ./node_modules/.pnpm_patches/package@version -``` diff --git a/patches/valaxy-theme-press@0.25.9.patch b/patches/valaxy-theme-press@0.25.9.patch deleted file mode 100644 index 580bae5eb..000000000 --- a/patches/valaxy-theme-press@0.25.9.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/components/PressCategory.vue b/components/PressCategory.vue -index b2ffedd2e205ffca4545bb355735463114d95088..b30ce35243b0eb6d727aa35d464a811eaec194f3 100644 ---- a/components/PressCategory.vue -+++ b/components/PressCategory.vue -@@ -58,7 +58,15 @@ const { $tO } = useValaxyI18n() - - - -- -+ - - - diff --git a/patches/valaxy@0.25.9.patch b/patches/valaxy@0.25.9.patch deleted file mode 100644 index 7d3851c4e..000000000 --- a/patches/valaxy@0.25.9.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/client/composables/categories.ts b/client/composables/categories.ts -index 47642a6f17c4dff71dca9306a669bfd748cd197a..900ecd0ef88cffb1b45ee5de4949a1054e506aa6 100644 ---- a/client/composables/categories.ts -+++ b/client/composables/categories.ts -@@ -67,6 +67,11 @@ export function useCategories(category?: MaybeRef, posts: Post[] = []) { - posts = site.postList - } - -+ posts.sort((a, b) => { -+ // console.log(a.top, b.top, 'sort post list') -+ return (b.top || 0) - (a.top || 0) -+ }) -+ - const categoryList: CategoryList = { - name: 'All', - total: posts.length, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index f54dcfbc1..000000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,8913 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -patchedDependencies: - valaxy-theme-press@0.25.9: - hash: a6c7859cddc4d023eca0c3ca0820314f6456487482af67a4b4ba28719d385f3d - path: patches/valaxy-theme-press@0.25.9.patch - valaxy@0.25.9: - hash: 4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611 - path: patches/valaxy@0.25.9.patch - -importers: - - .: - dependencies: - '@giscus/vue': - specifier: ^3.1.1 - version: 3.1.1(vue@3.5.17(typescript@5.8.3)) - '@iconify-json/devicon-plain': - specifier: ^1.2.31 - version: 1.2.31 - '@iconify-json/logos': - specifier: ^1.2.4 - version: 1.2.4 - '@iconify-json/ri': - specifier: ^1.2.5 - version: 1.2.5 - '@iconify-json/vscode-icons': - specifier: ^1.2.23 - version: 1.2.23 - valaxy: - specifier: 0.25.9 - version: 0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0) - valaxy-addon-algolia: - specifier: 0.2.0 - version: 0.2.0(@algolia/client-search@5.34.0)(search-insights@2.17.3)(valaxy@0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0)) - valaxy-addon-components: - specifier: 0.1.0 - version: 0.1.0 - valaxy-addon-git-log: - specifier: 0.3.2 - version: 0.3.2(valaxy@0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0)) - valaxy-theme-press: - specifier: 0.25.9 - version: 0.25.9(patch_hash=a6c7859cddc4d023eca0c3ca0820314f6456487482af67a4b4ba28719d385f3d)(@algolia/client-search@5.34.0)(search-insights@2.17.3) - vitepress-plugin-group-icons: - specifier: ^1.6.1 - version: 1.6.1(markdown-it@14.1.0)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - devDependencies: - '@iconify-json/carbon': - specifier: 1.2.9 - version: 1.2.9 - '@iconify-json/devicon': - specifier: ^1.2.32 - version: 1.2.32 - '@iconify-json/simple-icons': - specifier: 1.2.39 - version: 1.2.39 - '@types/node': - specifier: ^24.0.15 - version: 24.0.15 - husky: - specifier: ^9.1.7 - version: 9.1.7 - lint-staged: - specifier: ^16.1.2 - version: 16.1.2 - nodemon: - specifier: ^3.1.10 - version: 3.1.10 - prettier: - specifier: ^3.6.2 - version: 3.6.2 - vite: - specifier: 6.3.5 - version: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vitepress: - specifier: 1.1.4 - version: 1.1.4(@algolia/client-search@5.34.0)(@types/node@24.0.15)(axios@1.10.0)(fuse.js@7.1.0)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.89.2)(search-insights@2.17.3)(terser@5.43.1)(typescript@5.8.3) - vue: - specifier: ^3.5.17 - version: 3.5.17(typescript@5.8.3) - vue-i18n: - specifier: ^11.1.10 - version: 11.1.10(vue@3.5.17(typescript@5.8.3)) - vue-tsc: - specifier: ^2.2.12 - version: 2.2.12(typescript@5.8.3) - -packages: - - '@algolia/autocomplete-core@1.17.9': - resolution: {integrity: sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==} - - '@algolia/autocomplete-plugin-algolia-insights@1.17.9': - resolution: {integrity: sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ==} - peerDependencies: - search-insights: '>= 1 < 3' - - '@algolia/autocomplete-preset-algolia@1.17.9': - resolution: {integrity: sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - - '@algolia/autocomplete-shared@1.17.9': - resolution: {integrity: sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - - '@algolia/client-abtesting@5.34.0': - resolution: {integrity: sha512-d6ardhDtQsnMpyr/rPrS3YuIE9NYpY4rftkC7Ap9tyuhZ/+V3E/LH+9uEewPguKzVqduApdwJzYq2k+vAXVEbQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-analytics@5.34.0': - resolution: {integrity: sha512-WXIByjHNA106JO1Dj6b4viSX/yMN3oIB4qXr2MmyEmNq0MgfuPfPw8ayLRIZPa9Dp27hvM3G8MWJ4RG978HYFw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-common@5.34.0': - resolution: {integrity: sha512-JeN1XJLZIkkv6yK0KT93CIXXk+cDPUGNg5xeH4fN9ZykYFDWYRyqgaDo+qvg4RXC3WWkdQ+hogQuuCk4Y3Eotw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-insights@5.34.0': - resolution: {integrity: sha512-gdFlcQa+TWXJUsihHDlreFWniKPFIQ15i5oynCY4m9K3DCex5g5cVj9VG4Hsquxf2t6Y0yv8w6MvVTGDO8oRLw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-personalization@5.34.0': - resolution: {integrity: sha512-g91NHhIZDkh1IUeNtsUd8V/ZxuBc2ByOfDqhCkoQY3Z/mZszhpn3Czn6AR5pE81fx793vMaiOZvQVB5QttArkQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-query-suggestions@5.34.0': - resolution: {integrity: sha512-cvRApDfFrlJ3Vcn37U4Nd/7S6T8cx7FW3mVLJPqkkzixv8DQ/yV+x4VLirxOtGDdq3KohcIbIGWbg1QuyOZRvQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-search@5.34.0': - resolution: {integrity: sha512-m9tK4IqJmn+flEPRtuxuHgiHmrKV0su5fuVwVpq8/es4DMjWMgX1a7Lg1PktvO8AbKaTp9kTtBAPnwXpuCwmEg==} - engines: {node: '>= 14.0.0'} - - '@algolia/ingestion@1.34.0': - resolution: {integrity: sha512-2rxy4XoeRtIpzxEh5u5UgDC5HY4XbNdjzNgFx1eDrfFkSHpEVjirtLhISMy2N5uSFqYu1uUby5/NC1Soq8J7iw==} - engines: {node: '>= 14.0.0'} - - '@algolia/monitoring@1.34.0': - resolution: {integrity: sha512-OJiDhlJX8ZdWAndc50Z6aUEW/YmnhFK2ul3rahMw5/c9Damh7+oY9SufoK2LimJejy+65Qka06YPG29v2G/vww==} - engines: {node: '>= 14.0.0'} - - '@algolia/recommend@5.34.0': - resolution: {integrity: sha512-fzNQZAdVxu/Gnbavy8KW5gurApwdYcPW6+pjO7Pw8V5drCR3eSqnOxSvp79rhscDX8ezwqMqqK4F3Hsq+KpRzg==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-browser-xhr@5.34.0': - resolution: {integrity: sha512-gEI0xjzA/xvMpEdYmgQnf6AQKllhgKRtnEWmwDrnct+YPIruEHlx1dd7nRJTy/33MiYcCxkB4khXpNrHuqgp3Q==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-fetch@5.34.0': - resolution: {integrity: sha512-5SwGOttpbACT4jXzfSJ3mnTcF46SVNSnZ1JjxC3qBa3qKi4U0CJGzuVVy3L798u8dG5H0SZ2MAB5v7180Gnqew==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-node-http@5.34.0': - resolution: {integrity: sha512-409XlyIyEXrxyGjWxd0q5RASizHSRVUU0AXPCEdqnbcGEzbCgL1n7oYI8YxzE/RqZLha+PNwWCcTVn7EE5tyyQ==} - engines: {node: '>= 14.0.0'} - - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - - '@antfu/utils@0.7.10': - resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} - - '@antfu/utils@8.1.1': - resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} - - '@antfu/utils@9.2.0': - resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==} - - '@asamuzakjp/css-color@3.2.0': - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.28.0': - resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.28.0': - resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.28.0': - resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.27.1': - resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.27.1': - resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.27.3': - resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.27.1': - resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.27.6': - resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.28.0': - resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-proposal-decorators@7.28.0': - resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-decorators@7.27.1': - resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.27.1': - resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.27.1': - resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.27.1': - resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.28.0': - resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.28.0': - resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.28.1': - resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} - engines: {node: '>=6.9.0'} - - '@braintree/sanitize-url@7.1.1': - resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} - - '@chevrotain/cst-dts-gen@11.0.3': - resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} - - '@chevrotain/gast@11.0.3': - resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} - - '@chevrotain/regexp-to-ast@11.0.3': - resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} - - '@chevrotain/types@11.0.3': - resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} - - '@chevrotain/utils@11.0.3': - resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} - - '@clack/core@0.5.0': - resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} - - '@clack/prompts@0.11.0': - resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} - - '@csstools/color-helpers@5.0.2': - resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} - engines: {node: '>=18'} - - '@csstools/css-calc@2.1.4': - resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} - engines: {node: '>=18'} - peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-color-parser@3.0.10': - resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} - engines: {node: '>=18'} - peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-parser-algorithms@3.0.5': - resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} - engines: {node: '>=18'} - peerDependencies: - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-tokenizer@3.0.4': - resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} - engines: {node: '>=18'} - - '@docsearch/css@3.9.0': - resolution: {integrity: sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==} - - '@docsearch/js@3.9.0': - resolution: {integrity: sha512-4bKHcye6EkLgRE8ze0vcdshmEqxeiJM77M0JXjef7lrYZfSlMunrDOCqyLjiZyo1+c0BhUqA2QpFartIjuHIjw==} - - '@docsearch/react@3.9.0': - resolution: {integrity: sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ==} - peerDependencies: - '@types/react': '>= 16.8.0 < 20.0.0' - react: '>= 16.8.0 < 20.0.0' - react-dom: '>= 16.8.0 < 20.0.0' - search-insights: '>= 1 < 3' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - react-dom: - optional: true - search-insights: - optional: true - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} - 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.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.20.1': - resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.2.3': - resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.14.0': - resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.29.0': - resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.3.3': - resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@floating-ui/core@1.7.2': - resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} - - '@floating-ui/dom@1.1.1': - resolution: {integrity: sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==} - - '@floating-ui/utils@0.2.10': - resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - - '@giscus/vue@3.1.1': - resolution: {integrity: sha512-xgsc93MibRQ0d1glBxN1BrEREHTIf3BPYs/3R+UZHNqawFFoWBV6iJ7uVCXMqoEKZdE2c78B3aKt5gfGqo8I5A==} - peerDependencies: - vue: '>=3.2.0' - - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - - '@iconify-json/carbon@1.2.9': - resolution: {integrity: sha512-RyeSAuFTzhs1GX4yrzVEKEbNQGt95p9zMR4S2F63vbThtNoUr5OKwaWbhO/GbHQCSgdbKuZv2ApAOsY2fLxLbQ==} - - '@iconify-json/devicon-plain@1.2.31': - resolution: {integrity: sha512-c4j4UiGXGflMJ4Ek/SOsix7U1CZG6kJu8xEZIR51vJMUxJeZC0WvZKJ4/kc4skP4l7vSN1quHcUv+zcAQ+V+AA==} - - '@iconify-json/devicon@1.2.32': - resolution: {integrity: sha512-dFBGujAmK7XRAEXSd1rZ9ymv8Vu8F0tvYbgb+IdSA25L7hqXQACPvwKlIfj6/16hAzx3FhfUHvtrbsL62/G6jw==} - - '@iconify-json/logos@1.2.4': - resolution: {integrity: sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==} - - '@iconify-json/ri@1.2.5': - resolution: {integrity: sha512-kWGimOXMZrlYusjBKKXYOWcKhbOHusFsmrmRGmjS7rH0BpML5A9/fy8KHZqFOwZfC4M6amObQYbh8BqO5cMC3w==} - - '@iconify-json/simple-icons@1.2.39': - resolution: {integrity: sha512-XlhW73c4dHvUrwWckVY76HDjnaZ2fWKD6hNZtd5kuv23GC0g3Lu0MXnYscpkIYOeiXO+Gtlw8FM53J7C84mCtA==} - - '@iconify-json/vscode-icons@1.2.23': - resolution: {integrity: sha512-gFTcKecKra2/b5SbGDgHGI/l8CuikHyBPmqGlK+YCmS8AK72dtDQbUekdoACsju/3TYS37QvdPoOQwnyx2LdYg==} - - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - - '@iconify/utils@2.3.0': - resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} - - '@intlify/bundle-utils@10.0.1': - resolution: {integrity: sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==} - engines: {node: '>= 18'} - peerDependencies: - petite-vue-i18n: '*' - vue-i18n: '*' - peerDependenciesMeta: - petite-vue-i18n: - optional: true - vue-i18n: - optional: true - - '@intlify/core-base@11.1.10': - resolution: {integrity: sha512-JhRb40hD93Vk0BgMgDc/xMIFtdXPHoytzeK6VafBNOj6bb6oUZrGamXkBKecMsmGvDQQaPRGG2zpa25VCw8pyw==} - engines: {node: '>= 16'} - - '@intlify/message-compiler@11.1.10': - resolution: {integrity: sha512-TABl3c8tSLWbcD+jkQTyBhrnW251dzqW39MPgEUCsd69Ua3ceoimsbIzvkcPzzZvt1QDxNkenMht+5//V3JvLQ==} - engines: {node: '>= 16'} - - '@intlify/shared@11.1.10': - resolution: {integrity: sha512-6ZW/f3Zzjxfa1Wh0tYQI5pLKUtU+SY7l70pEG+0yd0zjcsYcK0EBt6Fz30Dy0tZhEqemziQQy2aNU3GJzyrMUA==} - engines: {node: '>= 16'} - - '@intlify/unplugin-vue-i18n@6.0.8': - resolution: {integrity: sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==} - engines: {node: '>= 18'} - peerDependencies: - petite-vue-i18n: '*' - vue: ^3.2.25 - vue-i18n: '*' - peerDependenciesMeta: - petite-vue-i18n: - optional: true - vue-i18n: - optional: true - - '@intlify/vue-i18n-extensions@8.0.0': - resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} - engines: {node: '>= 18'} - peerDependencies: - '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 - '@vue/compiler-dom': ^3.0.0 - vue: ^3.0.0 - vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 - peerDependenciesMeta: - '@intlify/shared': - optional: true - '@vue/compiler-dom': - optional: true - vue: - optional: true - vue-i18n: - optional: true - - '@jridgewell/gen-mapping@0.3.12': - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/source-map@0.3.10': - resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} - - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - - '@jridgewell/trace-mapping@0.3.29': - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - - '@kwsites/file-exists@1.1.1': - resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} - - '@kwsites/promise-deferred@1.1.1': - resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} - - '@lit-labs/ssr-dom-shim@1.4.0': - resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} - - '@lit/reactive-element@2.1.1': - resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} - - '@mdit-vue/plugin-component@2.1.4': - resolution: {integrity: sha512-fiLbwcaE6gZE4c8Mkdkc4X38ltXh/EdnuPE1hepFT2dLiW6I4X8ho2Wq7nhYuT8RmV4OKlCFENwCuXlKcpV/sw==} - - '@mdit-vue/plugin-frontmatter@2.1.4': - resolution: {integrity: sha512-mOlavV176njnozIf0UZGFYymmQ2LK5S1rjrbJ1uGz4Df59tu0DQntdE7YZXqmJJA9MiSx7ViCTUQCNPKg7R8Ow==} - - '@mdit-vue/types@2.1.4': - resolution: {integrity: sha512-QiGNZslz+zXUs2X8D11UQhB4KAMZ0DZghvYxa7+1B+VMLcDtz//XHpWbcuexjzE3kBXSxIUTPH3eSQCa0puZHA==} - - '@mermaid-js/parser@0.6.2': - resolution: {integrity: sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@octokit/auth-token@5.1.2': - resolution: {integrity: sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==} - engines: {node: '>= 18'} - - '@octokit/core@6.1.6': - resolution: {integrity: sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==} - engines: {node: '>= 18'} - - '@octokit/endpoint@10.1.4': - resolution: {integrity: sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==} - engines: {node: '>= 18'} - - '@octokit/graphql@8.2.2': - resolution: {integrity: sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==} - engines: {node: '>= 18'} - - '@octokit/openapi-types@24.2.0': - resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} - - '@octokit/openapi-types@25.1.0': - resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} - - '@octokit/plugin-paginate-rest@11.6.0': - resolution: {integrity: sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==} - engines: {node: '>= 18'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-request-log@5.3.1': - resolution: {integrity: sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==} - engines: {node: '>= 18'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-rest-endpoint-methods@13.5.0': - resolution: {integrity: sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw==} - engines: {node: '>= 18'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/request-error@6.1.8': - resolution: {integrity: sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==} - engines: {node: '>= 18'} - - '@octokit/request@9.2.4': - resolution: {integrity: sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA==} - engines: {node: '>= 18'} - - '@octokit/rest@21.1.1': - resolution: {integrity: sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==} - engines: {node: '>= 18'} - - '@octokit/types@13.10.0': - resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} - - '@octokit/types@14.1.0': - resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} - - '@parcel/watcher-android-arm64@2.5.1': - resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [android] - - '@parcel/watcher-darwin-arm64@2.5.1': - resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [darwin] - - '@parcel/watcher-darwin-x64@2.5.1': - resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [darwin] - - '@parcel/watcher-freebsd-x64@2.5.1': - resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [freebsd] - - '@parcel/watcher-linux-arm-glibc@2.5.1': - resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - - '@parcel/watcher-linux-arm-musl@2.5.1': - resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - - '@parcel/watcher-linux-arm64-glibc@2.5.1': - resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - - '@parcel/watcher-linux-arm64-musl@2.5.1': - resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - - '@parcel/watcher-linux-x64-glibc@2.5.1': - resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - - '@parcel/watcher-linux-x64-musl@2.5.1': - resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - - '@parcel/watcher-win32-arm64@2.5.1': - resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [win32] - - '@parcel/watcher-win32-ia32@2.5.1': - resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} - engines: {node: '>= 10.0.0'} - cpu: [ia32] - os: [win32] - - '@parcel/watcher-win32-x64@2.5.1': - resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [win32] - - '@parcel/watcher@2.5.1': - resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} - engines: {node: '>= 10.0.0'} - - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - - '@quansync/fs@0.1.3': - resolution: {integrity: sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg==} - engines: {node: '>=20.0.0'} - - '@rolldown/pluginutils@1.0.0-beta.19': - resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} - - '@rollup/pluginutils@5.2.0': - resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/rollup-android-arm-eabi@4.45.1': - resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.45.1': - resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.45.1': - resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.45.1': - resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.45.1': - resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.45.1': - resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': - resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.45.1': - resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.45.1': - resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.45.1': - resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': - resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': - resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.45.1': - resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.45.1': - resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.45.1': - resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.45.1': - resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.45.1': - resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-win32-arm64-msvc@4.45.1': - resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.45.1': - resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.45.1': - resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} - cpu: [x64] - os: [win32] - - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - - '@selderee/plugin-htmlparser2@0.11.0': - resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - - '@shikijs/core@1.29.2': - resolution: {integrity: sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==} - - '@shikijs/core@3.8.1': - resolution: {integrity: sha512-uTSXzUBQ/IgFcUa6gmGShCHr4tMdR3pxUiiWKDm8pd42UKJdYhkAYsAmHX5mTwybQ5VyGDgTjW4qKSsRvGSang==} - - '@shikijs/engine-javascript@1.29.2': - resolution: {integrity: sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==} - - '@shikijs/engine-javascript@3.8.1': - resolution: {integrity: sha512-rZRp3BM1llrHkuBPAdYAzjlF7OqlM0rm/7EWASeCcY7cRYZIrOnGIHE9qsLz5TCjGefxBFnwgIECzBs2vmOyKA==} - - '@shikijs/engine-oniguruma@1.29.2': - resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==} - - '@shikijs/engine-oniguruma@3.8.1': - resolution: {integrity: sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==} - - '@shikijs/langs@1.29.2': - resolution: {integrity: sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==} - - '@shikijs/langs@3.8.1': - resolution: {integrity: sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ==} - - '@shikijs/themes@1.29.2': - resolution: {integrity: sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==} - - '@shikijs/themes@3.8.1': - resolution: {integrity: sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ==} - - '@shikijs/transformers@1.29.2': - resolution: {integrity: sha512-NHQuA+gM7zGuxGWP9/Ub4vpbwrYCrho9nQCLcCPfOe3Yc7LOYwmSuhElI688oiqIXk9dlZwDiyAG9vPBTuPJMA==} - - '@shikijs/transformers@3.8.1': - resolution: {integrity: sha512-nmTyFfBrhJk6HJi118jes0wuWdfKXeVUq1Nq+hm8h6wbk1KUfvtg+LY/uDfxZD2VDItHO3QoINIs3NtoKBmgxw==} - - '@shikijs/types@1.29.2': - resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==} - - '@shikijs/types@3.8.1': - resolution: {integrity: sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - - '@types/d3-array@3.2.1': - resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} - - '@types/d3-axis@3.0.6': - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - - '@types/d3-brush@3.0.6': - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - - '@types/d3-chord@3.0.6': - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - - '@types/d3-contour@3.0.6': - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - - '@types/d3-delaunay@6.0.4': - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - - '@types/d3-dispatch@3.0.6': - resolution: {integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==} - - '@types/d3-drag@3.0.7': - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - - '@types/d3-dsv@3.0.7': - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - - '@types/d3-fetch@3.0.7': - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - - '@types/d3-force@3.0.10': - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} - - '@types/d3-format@3.0.4': - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - - '@types/d3-geo@3.1.0': - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - - '@types/d3-hierarchy@3.1.7': - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} - - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - - '@types/d3-polygon@3.0.2': - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - - '@types/d3-quadtree@3.0.6': - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - - '@types/d3-random@3.0.3': - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} - - '@types/d3-scale-chromatic@3.1.0': - resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} - - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - - '@types/d3-selection@3.0.11': - resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} - - '@types/d3-shape@3.1.7': - resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} - - '@types/d3-time-format@4.0.3': - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} - - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - - '@types/d3-transition@3.0.9': - resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} - - '@types/d3-zoom@3.0.8': - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - - '@types/d3@7.4.3': - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/geojson@7946.0.16': - resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - - '@types/http-proxy@1.17.16': - resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/katex@0.16.7': - resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} - - '@types/linkify-it@5.0.0': - resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - - '@types/markdown-it@14.1.2': - resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/mdurl@2.0.0': - resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - - '@types/node@24.0.15': - resolution: {integrity: sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==} - - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@types/web-bluetooth@0.0.20': - resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - - '@types/web-bluetooth@0.0.21': - resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} - - '@typescript-eslint/project-service@8.37.0': - resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/scope-manager@8.37.0': - resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.37.0': - resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/types@8.37.0': - resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.37.0': - resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.9.0' - - '@typescript-eslint/visitor-keys@8.37.0': - resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - - '@unhead/addons@2.0.12': - resolution: {integrity: sha512-z/zmWScb5NNTqC3NLY2+rHnMDv2uOWnwbGSOsBLyxT8KvcnV/RdESid8m8zhspHPmCgF79ETLw2XMXBSupDUJw==} - - '@unhead/dom@2.0.12': - resolution: {integrity: sha512-odruHzIYEixtM/lDLQG2t9Lv3iEyNxDW5uuXFGoNUVDFR4NJS/FWbBzCYJ0jszsNUp6BXAAWZevBcwbpgYfB2g==} - peerDependencies: - unhead: 2.0.12 - - '@unhead/schema-org@2.0.12': - resolution: {integrity: sha512-wsOjcTvNncJwTUouw84vku0dKSfYiWEKxqV9abzpbjOJ6+44woO7XKFGD7MdTcRgsfkjO1J0MLHaG7VEBwJkGg==} - peerDependencies: - '@unhead/react': 2.0.12 - '@unhead/solid-js': 2.0.12 - '@unhead/svelte': 2.0.12 - '@unhead/vue': 2.0.12 - peerDependenciesMeta: - '@unhead/react': - optional: true - '@unhead/solid-js': - optional: true - '@unhead/svelte': - optional: true - '@unhead/vue': - optional: true - - '@unhead/vue@2.0.12': - resolution: {integrity: sha512-WFaiCVbBd39FK6Bx3GQskhgT9s45Vjx6dRQegYheVwU1AnF+FAfJVgWbrl21p6fRJcLAFp0xDz6wE18JYBM0eQ==} - peerDependencies: - vue: '>=3.5.13' - - '@unocss/astro@66.3.3': - resolution: {integrity: sha512-q26EfadSMmEXZpWDKsJF9anBCfhYDmWljVpDZ2Wo8K48IbZMUXrWfiAiUc6ijE/A/rADfHk8bp3a3GE01t3I9A==} - peerDependencies: - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - peerDependenciesMeta: - vite: - optional: true - - '@unocss/cli@66.3.3': - resolution: {integrity: sha512-U0HoDcwi/DetqP5zDT3dfxG94pC3TI0PfxmpdTfPY7xEylIdLbV89fb70CvJVysDSQJIuw6TYwqS1ZlHoYNKTA==} - engines: {node: '>=14'} - hasBin: true - - '@unocss/config@66.3.3': - resolution: {integrity: sha512-D/UxnAmkabapqWU4tF85dWWhNfCUyNutWmd4AD2VsQRZOykufJedLV74r3Z3XhoPJn4IGr3BKZm5/rflf5viDg==} - engines: {node: '>=14'} - - '@unocss/core@66.3.3': - resolution: {integrity: sha512-6WFLd92TJelVQARtCGaF+EgEoHKIVe43gkGXVoWILu0HUDRWdhv+cpcyX0RTJV22Y976AxeneU7/zmhAh+CXNg==} - - '@unocss/extractor-arbitrary-variants@66.3.3': - resolution: {integrity: sha512-TXzjH6FcITQ8V2x7ETHgVOlAHf3ll/ysxL+W4fMROm8jP/o7jvsg36tRfOwU0sDGo/qoCPux82ix9e6/JW0oqQ==} - - '@unocss/inspector@66.3.3': - resolution: {integrity: sha512-NsK1WRWez2Mzk4+ophtBdXel8nGaPkIDa9lYSFMdKLF/1jNW23txeEL8CsD6/CK8K0BsR11rhLKhUrzyrjfBSQ==} - - '@unocss/postcss@66.3.3': - resolution: {integrity: sha512-VKq+BtfPIZbLeAeZFprtKZJAyFBOqA8qpQm+vmWBiBia70JzkwfF2SMNIHiGt022yRo9ZmjnI9uRTxSzqXUsUQ==} - engines: {node: '>=14'} - peerDependencies: - postcss: ^8.4.21 - - '@unocss/preset-attributify@66.3.3': - resolution: {integrity: sha512-22+0Cqqu09q+xHfZ3Wk8Coxe5m6PmpgWz4U5xrEC8056UfG3Q1KEqoCxy2wySJIq8SqxQ30Nlll7oMa31B8Krw==} - - '@unocss/preset-icons@66.3.3': - resolution: {integrity: sha512-Bmhiev05BN/horlgnyZ8gzQWZKd7oVpUBWD66X7U/dgkLdO6B5GIIsdO5Fi7JLeMDmyXm6vlYk0YQhiTbx8l9w==} - - '@unocss/preset-mini@66.3.3': - resolution: {integrity: sha512-pz8rgvHRYS/6fsZNtG7iArLzwANnLy5GkHY/lbuqkWhO2S2Nf7kpJCbR/uV/XeuFsLnYcZW3NLOmelfvZvJamA==} - - '@unocss/preset-tagify@66.3.3': - resolution: {integrity: sha512-L1Ez7Y4uBaW+wiv1BOQygpfhseSt3EZ53jqkl7fxl1EKVsJy6SuZgJxlXEHUYp9xYdSp6EHq2CfL8UevaR+loA==} - - '@unocss/preset-typography@66.3.3': - resolution: {integrity: sha512-aQXiGCObvWD9grfUpm0d5nzN+Cpvag0rHP39UjUKb0xSTzY09VzwDrua4kWVO5wJLNK6/L70osyhEgmC3qToxA==} - - '@unocss/preset-uno@66.3.3': - resolution: {integrity: sha512-Tiho4LidpuMHrB19GHTU6XrL0A5eFELHk9ebQ/3WeTy+K/9a6Hn5zsHJe5UCtOsEcUdKB33oZx0hXUp93hb/YQ==} - - '@unocss/preset-web-fonts@66.3.3': - resolution: {integrity: sha512-ysKZeC7TXxRiqnNL9GxZFGMKFAHXrcaqozPaEOIJ40dvzbJt8IMLyFndZkcFMcgDCV0pFh/y37mGxxxARO9+pQ==} - - '@unocss/preset-wind3@66.3.3': - resolution: {integrity: sha512-iXmjvPqvmPTo4z7epQDqHxzlGRsbLJEgfETqTrRJeagvFG7Gs+ajS8cQhbf6wL01dSRHjvhVXi3MsIvqfHHXOw==} - - '@unocss/preset-wind4@66.3.3': - resolution: {integrity: sha512-JSJTXVJel6kX+u4Ktt6JGnukYWYhKxmjgORTwclUpokRHgEoD+xsh0Rz4YGJ1fWSnzNslNQhWP9yDRByVPHWwA==} - - '@unocss/preset-wind@66.3.3': - resolution: {integrity: sha512-3Mxl/TDPcv8nNKdFe3WKdlXE6de+lCaaizEH86BILW3ZeyPU9aKzWcZIoxohla0a6zMxDQ2+Gf+7EwaOvpqo7Q==} - - '@unocss/reset@66.3.3': - resolution: {integrity: sha512-VIeR/mIcCL89/1uA1KM1QCYca4aeIGqEHMTJL1nCD4v+7wk6XhNXhsp5gMIHo+V804SUSmATWaeHTiKpiFu7AQ==} - - '@unocss/rule-utils@66.3.3': - resolution: {integrity: sha512-QKgVGV5nRRnK44/reUKFLAc5UGyl98vz3hrfk8JI8pVza58vmQWTdAB2rIpNJ5a5j+EkWfDOUlGQaOrIeYGLdg==} - engines: {node: '>=14'} - - '@unocss/transformer-attributify-jsx@66.3.3': - resolution: {integrity: sha512-ENNYFk5wrI4jlxn0tWGeR9QGxflAfZue3X2ABg0KSVOiYyIOsrHqtdoiLYkuCA9idRlBZPQxePJKcPWt1r/tYA==} - - '@unocss/transformer-compile-class@66.3.3': - resolution: {integrity: sha512-VTEFuwp3iajGWyEFwmO5LRvOjgZM1TK+4rX5Q79xyTAPkLAKgOa03Ne8+kU8oG0TQEa4mXVw6ul9McM7UBJh1w==} - - '@unocss/transformer-directives@66.3.3': - resolution: {integrity: sha512-11T7fmYk/XZcqFDn4qiIvs04mJhUtAoha5Y99bVE+L3byWa6BT4jb5aSAKk+24q5aynwgB++4RgfQxarj69WTw==} - - '@unocss/transformer-variant-group@66.3.3': - resolution: {integrity: sha512-uhK81pbJfXJFYaXxOoIFVEG8/Kx1iaAkTwRB6c+WNUfl9GiKyYQcrI7bETgCPPbg230Z68jVICBgBATeLJ31vQ==} - - '@unocss/vite@66.3.3': - resolution: {integrity: sha512-uu3smeEW6q36ri6vydRx2GiTGF5O/J80Fr4GLmLiwfpt2YnPHraO7XHVR5/mwG2Oz5Kov0uGvxVsdgxZABKRgw==} - peerDependencies: - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - - '@valaxyjs/devtools@0.25.9': - resolution: {integrity: sha512-QSZOem1SLbsUGPjXwcyYt+zlVp/eS1CLPtI91C5qHg7pZ1jRaF2LQaXdOXK9DdsqVW92/METMmQ/X02uwVjsXA==} - - '@valaxyjs/utils@0.25.9': - resolution: {integrity: sha512-z++/haNEVqgnWsbHtyGhdioQRch5QH294UssBIi5HJRwjiYTbTL/NOU9ZenPYbz0uJMwNR6jlGVHp99H1R3xLw==} - - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vue: ^3.2.25 - - '@vitejs/plugin-vue@6.0.0': - resolution: {integrity: sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - vue: ^3.2.25 - - '@volar/language-core@2.4.15': - resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} - - '@volar/source-map@2.4.15': - resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} - - '@volar/typescript@2.4.15': - resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} - - '@vue-macros/common@3.0.0-beta.15': - resolution: {integrity: sha512-DMgq/rIh1H20WYNWU7krIbEfJRYDDhy7ix64GlT4AVUJZZWCZ5pxiYVJR3A3GmWQPkn7Pg7i3oIiGqu4JGC65w==} - engines: {node: '>=20.18.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - peerDependenciesMeta: - vue: - optional: true - - '@vue/babel-helper-vue-transform-on@1.4.0': - resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==} - - '@vue/babel-plugin-jsx@1.4.0': - resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - peerDependenciesMeta: - '@babel/core': - optional: true - - '@vue/babel-plugin-resolve-type@1.4.0': - resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/compiler-core@3.5.17': - resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} - - '@vue/compiler-dom@3.5.17': - resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} - - '@vue/compiler-sfc@3.5.17': - resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==} - - '@vue/compiler-ssr@3.5.17': - resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==} - - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} - - '@vue/devtools-api@6.6.4': - resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - - '@vue/devtools-api@7.7.2': - resolution: {integrity: sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==} - - '@vue/devtools-api@7.7.7': - resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} - - '@vue/devtools-core@7.7.7': - resolution: {integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==} - peerDependencies: - vue: ^3.0.0 - - '@vue/devtools-kit@7.7.7': - resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} - - '@vue/devtools-shared@7.7.7': - resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} - - '@vue/language-core@2.2.12': - resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@vue/reactivity@3.5.17': - resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==} - - '@vue/runtime-core@3.5.17': - resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==} - - '@vue/runtime-dom@3.5.17': - resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==} - - '@vue/server-renderer@3.5.17': - resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==} - peerDependencies: - vue: 3.5.17 - - '@vue/shared@3.5.17': - resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} - - '@vueuse/core@10.11.1': - resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} - - '@vueuse/core@13.5.0': - resolution: {integrity: sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==} - peerDependencies: - vue: ^3.5.0 - - '@vueuse/integrations@10.11.1': - resolution: {integrity: sha512-Y5hCGBguN+vuVYTZmdd/IMXLOdfS60zAmDmFYc4BKBcMUPZH1n4tdyDECCPjXm0bNT3ZRUy1xzTLGaUje8Xyaw==} - peerDependencies: - async-validator: ^4 - axios: ^1 - change-case: ^4 - drauu: ^0.3 - focus-trap: ^7 - fuse.js: ^6 - idb-keyval: ^6 - jwt-decode: ^3 - nprogress: ^0.2 - qrcode: ^1.5 - sortablejs: ^1 - universal-cookie: ^6 - peerDependenciesMeta: - async-validator: - optional: true - axios: - optional: true - change-case: - optional: true - drauu: - optional: true - focus-trap: - optional: true - fuse.js: - optional: true - idb-keyval: - optional: true - jwt-decode: - optional: true - nprogress: - optional: true - qrcode: - optional: true - sortablejs: - optional: true - universal-cookie: - optional: true - - '@vueuse/integrations@13.5.0': - resolution: {integrity: sha512-7RACJySnlpl0MkSzxbtadioNGSX4TL5/Wl2cUy4nDq/XkeHwPYvVM880HJUSiap/FXhVEup9VKTM9y/n5UspAw==} - peerDependencies: - async-validator: ^4 - axios: ^1 - change-case: ^5 - drauu: ^0.4 - focus-trap: ^7 - fuse.js: ^7 - idb-keyval: ^6 - jwt-decode: ^4 - nprogress: ^0.2 - qrcode: ^1.5 - sortablejs: ^1 - universal-cookie: ^7 || ^8 - vue: ^3.5.0 - peerDependenciesMeta: - async-validator: - optional: true - axios: - optional: true - change-case: - optional: true - drauu: - optional: true - focus-trap: - optional: true - fuse.js: - optional: true - idb-keyval: - optional: true - jwt-decode: - optional: true - nprogress: - optional: true - qrcode: - optional: true - sortablejs: - optional: true - universal-cookie: - optional: true - - '@vueuse/metadata@10.11.1': - resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} - - '@vueuse/metadata@13.5.0': - resolution: {integrity: sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw==} - - '@vueuse/shared@10.11.1': - resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} - - '@vueuse/shared@13.5.0': - resolution: {integrity: sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g==} - peerDependencies: - vue: ^3.5.0 - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - algoliasearch@5.34.0: - resolution: {integrity: sha512-wioVnf/8uuG8Bmywhk5qKIQ3wzCCtmdvicPRb0fa3kKYGGoewfgDqLEaET1MV2NbTc3WGpPv+AgauLVBp1nB9A==} - engines: {node: '>= 14.0.0'} - - alien-signals@1.0.13: - resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} - - ansi-escapes@7.0.0: - resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} - engines: {node: '>=18'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - ansis@4.1.0: - resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} - engines: {node: '>=14'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - ast-kit@2.1.1: - resolution: {integrity: sha512-mfh6a7gKXE8pDlxTvqIc/syH/P3RkzbOF6LeHdcKztLEzYe6IMsRCL7N8vI7hqTGWNxpkCuuRTpT21xNWqhRtQ==} - engines: {node: '>=20.18.0'} - - ast-walker-scope@0.8.1: - resolution: {integrity: sha512-72XOdbzQCMKERvFrxAykatn2pu7osPNq/sNUzwcHdWzwPvOsNpPqkawfDXVvQbA2RT+ivtsMNjYdojTUZitt1A==} - engines: {node: '>=20.18.0'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - axios@1.10.0: - resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - beasties@0.3.5: - resolution: {integrity: sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==} - engines: {node: '>=14.0.0'} - - before-after-hook@3.0.2: - resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - birpc@2.5.0: - resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} - - blueimp-md5@2.19.0: - resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} - - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} - engines: {node: '>=18'} - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.25.1: - resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camel-case@4.1.2: - resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001727: - resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - charenc@0.0.2: - resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} - - chevrotain-allstar@0.3.1: - resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} - peerDependencies: - chevrotain: ^11.0.0 - - chevrotain@11.0.3: - resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - clean-css@5.3.3: - resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} - engines: {node: '>= 10.0'} - - cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - cliui@9.0.1: - resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} - engines: {node: '>=20'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@14.0.0: - resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} - engines: {node: '>=20'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - - confbox@0.2.2: - resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - - consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - copy-anything@3.0.5: - resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} - engines: {node: '>=12.13'} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - - cose-base@1.0.3: - resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} - - cose-base@2.2.0: - resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - crypt@0.0.2: - resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} - - css-i18n@0.0.5: - resolution: {integrity: sha512-iIBukKoe2xPzecUUEeIsTp7jr78A55+BKMgM18esXni4i3J4//qadHlAyvkgO6/4VsAa+eLF8uOMqlhvGNSjfg==} - - css-select@6.0.0: - resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} - - css-tree@3.1.0: - resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - - css-what@7.0.0: - resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} - engines: {node: '>= 6'} - - cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} - engines: {node: '>=18'} - - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - cytoscape-cose-bilkent@4.1.0: - resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape-fcose@2.2.0: - resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape@3.32.1: - resolution: {integrity: sha512-dbeqFTLYEwlFg7UGtcZhCCG/2WayX72zK3Sq323CEX29CY81tYfVhw1MIdduCtpstB0cTOhJswWlM/OEB3Xp+Q==} - engines: {node: '>=0.10'} - - d3-array@2.12.1: - resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} - - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-axis@3.0.0: - resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} - engines: {node: '>=12'} - - d3-brush@3.0.0: - resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} - engines: {node: '>=12'} - - d3-chord@3.0.1: - resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-contour@4.0.2: - resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} - engines: {node: '>=12'} - - d3-delaunay@6.0.4: - resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} - engines: {node: '>=12'} - - d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - - d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} - - d3-dsv@3.0.1: - resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} - engines: {node: '>=12'} - hasBin: true - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-fetch@3.0.1: - resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} - engines: {node: '>=12'} - - d3-force@3.0.0: - resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} - engines: {node: '>=12'} - - d3-format@3.1.0: - resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} - engines: {node: '>=12'} - - d3-geo@3.1.1: - resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} - engines: {node: '>=12'} - - d3-hierarchy@3.1.2: - resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@1.0.9: - resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-polygon@3.0.1: - resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} - engines: {node: '>=12'} - - d3-quadtree@3.0.1: - resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} - engines: {node: '>=12'} - - d3-random@3.0.1: - resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} - engines: {node: '>=12'} - - d3-sankey@0.12.3: - resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} - - d3-scale-chromatic@3.1.0: - resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - - d3-shape@1.3.7: - resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} - - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} - - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - - d3-transition@3.0.1: - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 - - d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} - - d3@7.9.0: - resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} - engines: {node: '>=12'} - - dagre-d3-es@7.0.11: - resolution: {integrity: sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==} - - data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} - - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - default-browser-id@5.0.0: - resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} - engines: {node: '>=18'} - - default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} - engines: {node: '>=18'} - - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - - delaunator@5.0.1: - resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - - detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - dompurify@3.2.6: - resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==} - - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - - dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - electron-to-chromium@1.5.187: - resolution: {integrity: sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==} - - email-validator@2.0.4: - resolution: {integrity: sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==} - engines: {node: '>4.0'} - - emoji-regex-xs@1.0.0: - resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} - - emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - - environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - - error-stack-parser-es@0.1.5: - resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@9.29.0: - resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - - execa@9.6.0: - resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} - engines: {node: ^18.19.0 || >=20.5.0} - - exsolve@1.0.7: - resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} - - extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - - fast-content-type-parse@2.0.1: - resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - fdir@6.4.6: - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - feed@5.1.0: - resolution: {integrity: sha512-qGNhgYygnefSkAHHrNHqC7p3R8J0/xQDS/cYUud8er/qD9EFGWyCdUDfULHTJQN1d3H3WprzVwMc9MfB4J50Wg==} - engines: {node: '>=20', pnpm: '>=10'} - - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - floating-vue@5.2.2: - resolution: {integrity: sha512-afW+h2CFafo+7Y9Lvw/xsqjaQlKLdJV7h1fCHfcYQ1C4SVMlu7OAekqWgu5d4SgvkBVU0pVpLlVsrSTBURFRkg==} - peerDependencies: - '@nuxt/kit': ^3.2.0 - vue: ^3.2.0 - peerDependenciesMeta: - '@nuxt/kit': - optional: true - - focus-trap@7.6.5: - resolution: {integrity: sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==} - - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} - engines: {node: '>= 6'} - - fs-extra@11.3.0: - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} - engines: {node: '>=14.14'} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - fuse.js@7.1.0: - resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} - engines: {node: '>=10'} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-east-asian-width@1.3.0: - resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} - engines: {node: '>=18'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - - giscus@1.6.0: - resolution: {integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} - engines: {node: '>=18'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - gravatar@1.8.2: - resolution: {integrity: sha512-GdRwLM3oYpFQKy47MKuluw9hZ2gaCtiKPbDGdcDEuYDKlc8eNnW27KYL9LVbIDzEsx88WtDWQm2ClBcsgBnj6w==} - engines: {node: '>=10'} - hasBin: true - - gray-matter@4.0.3: - resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} - engines: {node: '>=6.0'} - - gzip-size@6.0.0: - resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} - engines: {node: '>=10'} - - hachure-fill@0.5.2: - resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - hookable@5.5.3: - resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - - html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} - - html-minifier-terser@7.2.0: - resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} - engines: {node: ^14.13.1 || >=16.0.0} - hasBin: true - - html-to-text@9.0.5: - resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} - engines: {node: '>=14'} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - html5parser@2.0.2: - resolution: {integrity: sha512-L0y+IdTVxHsovmye8MBtFgBvWZnq1C9WnI/SmJszxoQjmUH1psX2uzDk21O5k5et6udxdGjwxkbmT9eVRoG05w==} - - htmlparser2@10.0.0: - resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - - htmlparser2@8.0.2: - resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - - http-proxy-middleware@3.0.5: - resolution: {integrity: sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - http-proxy@1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} - engines: {node: '>=8.0.0'} - - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - - human-signals@8.0.1: - resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} - engines: {node: '>=18.18.0'} - - husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} - engines: {node: '>=18'} - hasBin: true - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - immutable@5.1.3: - resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - internmap@1.0.1: - resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} - - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-buffer@1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - - is-fullwidth-code-point@5.0.0: - resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} - engines: {node: '>=18'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - - is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - - is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} - engines: {node: '>=12.13'} - - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} - engines: {node: '>=10'} - hasBin: true - - jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} - hasBin: true - - js-base64@3.7.7: - resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - jsdom@26.1.0: - resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} - engines: {node: '>=18'} - peerDependencies: - canvas: ^3.0.0 - peerDependenciesMeta: - canvas: - optional: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonc-eslint-parser@2.4.0: - resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - - katex@0.16.22: - resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} - hasBin: true - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - khroma@2.1.0: - resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - - langium@3.3.1: - resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} - engines: {node: '>=16.0.0'} - - layout-base@1.0.2: - resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} - - layout-base@2.0.1: - resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - - leac@0.6.0: - resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} - - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - - lint-staged@16.1.2: - resolution: {integrity: sha512-sQKw2Si2g9KUZNY3XNvRuDq4UJqpHwF0/FQzZR2M7I5MvtpWvibikCjUVJzZdGE0ByurEl3KQNvsGetd1ty1/Q==} - engines: {node: '>=20.17'} - hasBin: true - - listr2@8.3.3: - resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} - engines: {node: '>=18.0.0'} - - lit-element@4.2.1: - resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} - - lit-html@3.3.1: - resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} - - lit@3.3.1: - resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==} - - local-pkg@0.5.1: - resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} - engines: {node: '>=14'} - - local-pkg@1.1.1: - resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} - engines: {node: '>=14'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - - log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} - engines: {node: '>=18'} - - log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} - - lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} - engines: {node: 20 || >=22} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - magic-string-ast@1.0.0: - resolution: {integrity: sha512-8rbuNizut2gW94kv7pqgt0dvk+AHLPVIm0iJtpSgQJ9dx21eWx5SBel8z3jp1xtC0j6/iyK3AWGhAR1H61s7LA==} - engines: {node: '>=20.18.0'} - - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - - mark.js@8.11.1: - resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} - - markdown-it-anchor@9.2.0: - resolution: {integrity: sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg==} - peerDependencies: - '@types/markdown-it': '*' - markdown-it: '*' - - markdown-it-async@2.2.0: - resolution: {integrity: sha512-sITME+kf799vMeO/ww/CjH6q+c05f6TLpn6VOmmWCGNqPJzSh+uFgZoMB9s0plNtW6afy63qglNAC3MhrhP/gg==} - - markdown-it-attrs@4.3.1: - resolution: {integrity: sha512-/ko6cba+H6gdZ0DOw7BbNMZtfuJTRp9g/IrGIuz8lYc/EfnmWRpaR3CFPnNbVz0LDvF8Gf1hFGPqrQqq7De0rg==} - engines: {node: '>=6'} - peerDependencies: - markdown-it: '>= 9.0.0' - - markdown-it-container@4.0.0: - resolution: {integrity: sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==} - - markdown-it-emoji@3.0.0: - resolution: {integrity: sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==} - - markdown-it-footnote@4.0.0: - resolution: {integrity: sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==} - - markdown-it-image-figures@2.1.1: - resolution: {integrity: sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g==} - engines: {node: '>=12.0.0'} - peerDependencies: - markdown-it: '*' - - markdown-it-table-of-contents@0.9.0: - resolution: {integrity: sha512-PKtgvLweW/PXj8Pg2EfJM9qANbYqchxOiZy+70kLKFfCL4IkdlMzc53Z4Yj2ABw45MEzrXUSHh+4Cs2tDjwEig==} - engines: {node: '>6.4.0'} - - markdown-it-task-lists@2.1.1: - resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==} - - markdown-it@14.1.0: - resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} - hasBin: true - - marked@16.1.1: - resolution: {integrity: sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==} - engines: {node: '>= 20'} - hasBin: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - md5@2.3.0: - resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - - mdast-util-to-hast@13.2.0: - resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - - mdn-data@2.12.2: - resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - medium-zoom@1.1.0: - resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - mermaid@11.9.0: - resolution: {integrity: sha512-YdPXn9slEwO0omQfQIsW6vS84weVQftIyyTGAZCwM//MGhPzL1+l6vO6bkf0wnP4tHigH1alZ5Ooy3HXI2gOag==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} - - mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minisearch@6.3.0: - resolution: {integrity: sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - mlly@1.7.4: - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} - - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - muggle-string@0.4.1: - resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - - nano-spawn@1.0.2: - resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} - engines: {node: '>=20.17'} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@5.1.5: - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} - engines: {node: ^18 || >=20} - hasBin: true - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - - node-addon-api@7.1.1: - resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - - node-fetch-native@1.6.6: - resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} - - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - - nodemon@3.1.10: - resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==} - engines: {node: '>=10'} - hasBin: true - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - - nprogress@0.2.0: - resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - nwsapi@2.2.20: - resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - ofetch@1.4.1: - resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} - - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - - oniguruma-parser@0.12.1: - resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} - - oniguruma-to-es@2.3.0: - resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==} - - oniguruma-to-es@4.3.3: - resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} - - open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} - engines: {node: '>=18'} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-manager-detector@1.3.0: - resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} - - param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - - parseley@0.12.1: - resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} - - pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - - pascalcase@2.0.0: - resolution: {integrity: sha512-DHpENy5Qm/FaX+x3iBLoMLG/XHNCTgL+yErm1TwuVaj6u4fiOSkYkf60vGtITk7hrKHOO4uCl9vRrD4hqjNKjg==} - engines: {node: '>=14'} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-data-parser@0.1.0: - resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - peberminta@0.9.0: - resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - - pinia@3.0.3: - resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==} - peerDependencies: - typescript: '>=4.4.4' - vue: ^2.7.0 || ^3.5.11 - peerDependenciesMeta: - typescript: - optional: true - - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - - pkg-types@2.2.0: - resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} - - pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} - - points-on-curve@0.2.0: - resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} - - points-on-path@0.2.1: - resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - - postcss-media-query-parser@0.2.3: - resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - - preact@10.26.9: - resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} - engines: {node: '>=14'} - hasBin: true - - pretty-ms@9.2.0: - resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} - engines: {node: '>=18'} - - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - qrcode@1.5.4: - resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} - engines: {node: '>=10.13.0'} - hasBin: true - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - quansync@0.2.10: - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} - - querystring@0.2.0: - resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - raw-body@3.0.0: - resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} - engines: {node: '>= 0.8'} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - regex-recursion@5.1.1: - resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==} - - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@5.1.1: - resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==} - - regex@6.0.1: - resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} - - relateurl@0.2.7: - resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} - engines: {node: '>= 0.10'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-global@2.0.0: - resolution: {integrity: sha512-gnAQ0Q/KkupGkuiMyX4L0GaBV8iFwlmoXsMtOz+DFTaKmHhOO/dSlP1RMKhpvHv/dh6K/IQkowGJBqUG0NfBUw==} - engines: {node: '>=18'} - - restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - - robust-predicates@3.0.2: - resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - - rollup@4.45.1: - resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - roughjs@4.6.6: - resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - - rrweb-cssom@0.8.0: - resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - - run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} - engines: {node: '>=18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rw@1.3.3: - resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - sass@1.89.2: - resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==} - engines: {node: '>=14.0.0'} - hasBin: true - - sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - - saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - - scule@1.3.0: - resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} - - search-insights@2.17.3: - resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} - - section-matter@1.0.0: - resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} - engines: {node: '>=4'} - - selderee@0.11.0: - resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shiki@1.29.2: - resolution: {integrity: sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==} - - shiki@3.8.1: - resolution: {integrity: sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg==} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-git@3.28.0: - resolution: {integrity: sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==} - - simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} - engines: {node: '>=18'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - - slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - - slice-ansi@7.1.0: - resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} - engines: {node: '>=18'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - speakingurl@14.0.1: - resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} - engines: {node: '>=0.10.0'} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - star-markdown-css@0.5.3: - resolution: {integrity: sha512-ewlx4ge1i6B2jopgOJbhgTbshNFI8HEH5vD2itxDh3qKYz+a9+H2EJC1pyPapDeJEOm0DRpHiAvBOSB/H1RMvA==} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - - string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom-string@1.0.0: - resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} - engines: {node: '>=0.10.0'} - - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - - superjson@2.2.2: - resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} - engines: {node: '>=16'} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - - table@6.9.0: - resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} - engines: {node: '>=10.0.0'} - - terser@5.43.1: - resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} - engines: {node: '>=10'} - hasBin: true - - tinyexec@1.0.1: - resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} - engines: {node: '>=12.0.0'} - - tldts-core@6.1.86: - resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} - - tldts@6.1.86: - resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} - hasBin: true - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - - tough-cookie@5.1.2: - resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} - engines: {node: '>=16'} - - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - ts-dedent@2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - - unconfig@7.3.2: - resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==} - - undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} - - unhead@2.0.12: - resolution: {integrity: sha512-5oo0lwz81XDXCmrHGzgmbaNOxM8R9MZ3FkEs2ROHeW8e16xsrv7qXykENlISrcxr3RLPHQEsD1b6js9P2Oj/Ow==} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - - universal-user-agent@7.0.3: - resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unocss@66.3.3: - resolution: {integrity: sha512-HSB+K4/EbouwYmxpPU52cg0exua7PUr2IAJZBV3iai6tPdMcJ0c8jXaw7G+2L+ffruVFTcS0e2kE4OrR8BKDLg==} - engines: {node: '>=14'} - peerDependencies: - '@unocss/webpack': 66.3.3 - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - peerDependenciesMeta: - '@unocss/webpack': - optional: true - vite: - optional: true - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - unplugin-ast@0.15.1: - resolution: {integrity: sha512-7nhKrf61dXPU7vqzu3yxHs0YaUToIYB32FlnNLxdAOjF+bI+16LpDygGtdRuhJKgwPCpz0tNaAEamxaGB/SQOQ==} - engines: {node: '>=20.18.0'} - - unplugin-utils@0.2.4: - resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==} - engines: {node: '>=18.12.0'} - - unplugin-vue-components@28.0.0: - resolution: {integrity: sha512-vYe0wSyqTVhyNFIad1iiGyQGhG++tDOMgohqenMDOAooMJP9vvzCdXTqCVx20A0rCQXFNjgoRbSeDAioLPH36Q==} - engines: {node: '>=14'} - peerDependencies: - '@babel/parser': ^7.15.8 - '@nuxt/kit': ^3.2.2 - vue: 2 || 3 - peerDependenciesMeta: - '@babel/parser': - optional: true - '@nuxt/kit': - optional: true - - unplugin-vue-markdown@29.1.0: - resolution: {integrity: sha512-BvDFrhsiXzVvzfq1y68jtZwHg1NYJBteSXmUK4zMdX1HT2QtKw8yimjUbDwuI1K9TW/1/L6QYiRQSl1OkHcxWg==} - engines: {node: '>=20'} - peerDependencies: - vite: ^2.0.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 - - unplugin-vue-router@0.14.0: - resolution: {integrity: sha512-ipjunvS5e2aFHBAUFuLbHl2aHKbXXXBhTxGT9wZx66fNVPdEQzVVitF8nODr1plANhTTa3UZ+DQu9uyLngMzoQ==} - peerDependencies: - '@vue/compiler-sfc': ^3.5.17 - vue-router: ^4.5.1 - peerDependenciesMeta: - vue-router: - optional: true - - unplugin@1.16.1: - resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} - engines: {node: '>=14.0.0'} - - unplugin@2.3.5: - resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==} - engines: {node: '>=18.12.0'} - - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - valaxy-addon-algolia@0.2.0: - resolution: {integrity: sha512-7Swjt3nrljjJ+1U80lZw3zN8cMj/7z8V71CJBRWhHKtGYDlNNv5g2EkvRJbI6O3Bpvvnb9iOC0K5ST6oRn7ReQ==} - peerDependencies: - valaxy: '>=0.22' - - valaxy-addon-components@0.1.0: - resolution: {integrity: sha512-7uCJgUgc2JHISmz/UDQlhBqGSNW4QKmkiRlcwj5WhjhScC/KktK8JtADeKh6FCaAJHbjShovHB/9UPVtnTwORg==} - - valaxy-addon-git-log@0.3.2: - resolution: {integrity: sha512-2gJPX6+gXYvyMtoboLORuFrsE9Bh+jz5cy8zjXfW2kDJdxmyJI8rqpP8WBHepssbU2DRUauK3cS+O+Xhnchs3A==} - peerDependencies: - valaxy: '>=0.22' - - valaxy-theme-press@0.25.9: - resolution: {integrity: sha512-m3/ELaJGpMStqDc5JFcB+3U6VTc4wGsDN1sfwdvajh1T7RRryVsZHKBbD0kMA9JXtNkn3GGpAs0wU1eIuJ6Oaw==} - - valaxy@0.25.9: - resolution: {integrity: sha512-FmfVaQdpfMls4hWu8Z8G8An8RaKNK8kX6FnZ1jC+acPRtfyDNAqhdBBCIqktSZXXcmYosWIrPsJqzXo/r5TCgg==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - - vanilla-lazyload@19.1.3: - resolution: {integrity: sha512-bBMERPu2AFJc35krS+8BOhq++c6dRfL6q368lJPnkS5U92fRQagTR3FsNta69/GukfZzDwDEjD5M3U7VuSiCDw==} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - vite-dev-rpc@1.1.0: - resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==} - peerDependencies: - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 - - vite-hot-client@2.1.0: - resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} - peerDependencies: - vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - - vite-plugin-inspect@0.8.9: - resolution: {integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': '*' - vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1 - peerDependenciesMeta: - '@nuxt/kit': - optional: true - - vite-plugin-vue-devtools@7.7.7: - resolution: {integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==} - engines: {node: '>=v14.21.3'} - peerDependencies: - vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - - vite-plugin-vue-inspector@5.3.2: - resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==} - peerDependencies: - vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - - vite-plugin-vue-layouts@0.11.0: - resolution: {integrity: sha512-uh6NW7lt+aOXujK4eHfiNbeo55K9OTuB7fnv+5RVc4OBn/cZull6ThXdYH03JzKanUfgt6QZ37NbbtJ0og59qw==} - peerDependencies: - vite: ^4.0.0 || ^5.0.0 - vue: ^3.2.4 - vue-router: ^4.0.11 - - vite-ssg-sitemap@0.9.0: - resolution: {integrity: sha512-C5XCa+oPET2w/re92L9o7je4LMsKJDOC/TI6klo0xAi72nlgQKa7rt9cOYMZxgAsAJUhk+c6RYPTRc73xvn1vw==} - - vite-ssg@28.0.0: - resolution: {integrity: sha512-idYdFXRi1XdiE4fd1g6n/NIpGn6KzMX4Lzrq2Ynr+j2FTd0UMuBlMJh6Z4kB6ux4lBonTOU2tTV2hhL/ubQ/1w==} - engines: {node: '>=20.0.0'} - hasBin: true - peerDependencies: - beasties: ^0.2.0 - prettier: ^3.3.0 - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0-0 - vue: ^3.2.10 - vue-router: ^4.0.1 - peerDependenciesMeta: - beasties: - optional: true - prettier: - optional: true - vue-router: - optional: true - - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vite@6.3.5: - resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vite@7.0.5: - resolution: {integrity: sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitepress-plugin-group-icons@1.6.1: - resolution: {integrity: sha512-eoFlFAhAy/yTZDbaIgA/nMbjVYXkf8pz8rr75MN2VCw7yH60I3cw6bW5EuwddAeafZtBqbo8OsEGU7TIWFiAjg==} - peerDependencies: - markdown-it: '>=14' - vite: '>=3' - - vitepress@1.1.4: - resolution: {integrity: sha512-bWIzFZXpPB6NIDBuWnS20aMADH+FcFKDfQNYFvbOWij03PR29eImTceQHIzCKordjXYBhM/TjE5VKFTUJ3EheA==} - hasBin: true - peerDependencies: - markdown-it-mathjax3: ^4 - postcss: ^8 - peerDependenciesMeta: - markdown-it-mathjax3: - optional: true - postcss: - optional: true - - vscode-jsonrpc@8.2.0: - resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} - engines: {node: '>=14.0.0'} - - vscode-languageserver-protocol@3.17.5: - resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - - vscode-languageserver-textdocument@1.0.12: - resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} - - vscode-languageserver-types@3.17.5: - resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - - vscode-languageserver@9.0.1: - resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} - hasBin: true - - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - - vue-demi@0.14.10: - resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - vue-flow-layout@0.1.1: - resolution: {integrity: sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==} - peerDependencies: - vue: ^3.4.37 - - vue-i18n@11.1.10: - resolution: {integrity: sha512-C+IwnSg8QDSOAox0gdFYP5tsKLx5jNWxiawNoiNB/Tw4CReXmM1VJMXbduhbrEzAFLhreqzfDocuSVjGbxQrag==} - engines: {node: '>= 16'} - peerDependencies: - vue: ^3.0.0 - - vue-resize@2.0.0-alpha.1: - resolution: {integrity: sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==} - peerDependencies: - vue: ^3.0.0 - - vue-router@4.5.1: - resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} - peerDependencies: - vue: ^3.2.0 - - vue-tsc@2.2.12: - resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} - hasBin: true - peerDependencies: - typescript: '>=5.0.0' - - vue@3.5.17: - resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} - engines: {node: '>=18'} - - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xml-js@1.6.11: - resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} - hasBin: true - - xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} - - xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yaml-eslint-parser@1.3.0: - resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==} - engines: {node: ^14.17.0 || >=16.0.0} - - yaml@2.8.0: - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} - engines: {node: '>= 14.6'} - hasBin: true - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yoctocolors@2.1.1: - resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} - engines: {node: '>=18'} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0)(search-insights@2.17.3) - '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0) - transitivePeerDependencies: - - '@algolia/client-search' - - algoliasearch - - search-insights - - '@algolia/autocomplete-plugin-algolia-insights@1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0) - search-insights: 2.17.3 - transitivePeerDependencies: - - '@algolia/client-search' - - algoliasearch - - '@algolia/autocomplete-preset-algolia@1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0)': - dependencies: - '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0) - '@algolia/client-search': 5.34.0 - algoliasearch: 5.34.0 - - '@algolia/autocomplete-shared@1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0)': - dependencies: - '@algolia/client-search': 5.34.0 - algoliasearch: 5.34.0 - - '@algolia/client-abtesting@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/client-analytics@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/client-common@5.34.0': {} - - '@algolia/client-insights@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/client-personalization@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/client-query-suggestions@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/client-search@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/ingestion@1.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/monitoring@1.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/recommend@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - '@algolia/requester-browser-xhr@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - - '@algolia/requester-fetch@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - - '@algolia/requester-node-http@5.34.0': - dependencies: - '@algolia/client-common': 5.34.0 - - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 - - '@antfu/install-pkg@1.1.0': - dependencies: - package-manager-detector: 1.3.0 - tinyexec: 1.0.1 - - '@antfu/utils@0.7.10': {} - - '@antfu/utils@8.1.1': {} - - '@antfu/utils@9.2.0': {} - - '@asamuzakjp/css-color@3.2.0': - dependencies: - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - lru-cache: 10.4.3 - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.28.0': {} - - '@babel/core@7.28.0': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.0 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) - '@babel/helpers': 7.27.6 - '@babel/parser': 7.28.0 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.1 - convert-source-map: 2.0.0 - debug: 4.4.1(supports-color@5.5.0) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.28.0': - dependencies: - '@babel/parser': 7.28.0 - '@babel/types': 7.28.1 - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 - jsesc: 3.1.0 - - '@babel/helper-annotate-as-pure@7.27.3': - dependencies: - '@babel/types': 7.28.1 - - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.28.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.27.1 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.0 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-member-expression-to-functions@7.27.1': - dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.27.1': - dependencies: - '@babel/types': 7.28.1 - - '@babel/helper-plugin-utils@7.27.1': {} - - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-member-expression-to-functions': 7.27.1 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.27.1': {} - - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.27.6': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.1 - - '@babel/parser@7.28.0': - dependencies: - '@babel/types': 7.28.1 - - '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)': - dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) - transitivePeerDependencies: - - supports-color - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.0 - '@babel/types': 7.28.1 - - '@babel/traverse@7.28.0': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.0 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.0 - '@babel/template': 7.27.2 - '@babel/types': 7.28.1 - debug: 4.4.1(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - '@babel/types@7.28.1': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - - '@braintree/sanitize-url@7.1.1': {} - - '@chevrotain/cst-dts-gen@11.0.3': - dependencies: - '@chevrotain/gast': 11.0.3 - '@chevrotain/types': 11.0.3 - lodash-es: 4.17.21 - - '@chevrotain/gast@11.0.3': - dependencies: - '@chevrotain/types': 11.0.3 - lodash-es: 4.17.21 - - '@chevrotain/regexp-to-ast@11.0.3': {} - - '@chevrotain/types@11.0.3': {} - - '@chevrotain/utils@11.0.3': {} - - '@clack/core@0.5.0': - dependencies: - picocolors: 1.1.1 - sisteransi: 1.0.5 - - '@clack/prompts@0.11.0': - dependencies: - '@clack/core': 0.5.0 - picocolors: 1.1.1 - sisteransi: 1.0.5 - - '@csstools/color-helpers@5.0.2': {} - - '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': - dependencies: - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': - dependencies: - '@csstools/color-helpers': 5.0.2 - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': - dependencies: - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/css-tokenizer@3.0.4': {} - - '@docsearch/css@3.9.0': {} - - '@docsearch/js@3.9.0(@algolia/client-search@5.34.0)(search-insights@2.17.3)': - dependencies: - '@docsearch/react': 3.9.0(@algolia/client-search@5.34.0)(search-insights@2.17.3) - preact: 10.26.9 - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/react' - - react - - react-dom - - search-insights - - '@docsearch/react@3.9.0(@algolia/client-search@5.34.0)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-core': 1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0)(search-insights@2.17.3) - '@algolia/autocomplete-preset-algolia': 1.17.9(@algolia/client-search@5.34.0)(algoliasearch@5.34.0) - '@docsearch/css': 3.9.0 - algoliasearch: 5.34.0 - optionalDependencies: - search-insights: 2.17.3 - transitivePeerDependencies: - - '@algolia/client-search' - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.25.8': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.25.8': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.25.8': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.25.8': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.25.8': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.25.8': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.25.8': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.25.8': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.25.8': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-arm@0.25.8': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.25.8': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.25.8': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.25.8': - optional: true - - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.25.8': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.25.8': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.25.8': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/linux-x64@0.25.8': - optional: true - - '@esbuild/netbsd-arm64@0.25.8': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.25.8': - optional: true - - '@esbuild/openbsd-arm64@0.25.8': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.25.8': - optional: true - - '@esbuild/openharmony-arm64@0.25.8': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.25.8': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.25.8': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.25.8': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.25.8': - optional: true - - '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@2.4.2))': - dependencies: - eslint: 9.29.0(jiti@2.4.2) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.1': {} - - '@eslint/config-array@0.20.1': - dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1(supports-color@5.5.0) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.2.3': {} - - '@eslint/core@0.14.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/core@0.15.1': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.3.1': - dependencies: - ajv: 6.12.6 - debug: 4.4.1(supports-color@5.5.0) - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.29.0': {} - - '@eslint/object-schema@2.1.6': {} - - '@eslint/plugin-kit@0.3.3': - dependencies: - '@eslint/core': 0.15.1 - levn: 0.4.1 - - '@floating-ui/core@1.7.2': - dependencies: - '@floating-ui/utils': 0.2.10 - - '@floating-ui/dom@1.1.1': - dependencies: - '@floating-ui/core': 1.7.2 - - '@floating-ui/utils@0.2.10': {} - - '@giscus/vue@3.1.1(vue@3.5.17(typescript@5.8.3))': - dependencies: - giscus: 1.6.0 - vue: 3.5.17(typescript@5.8.3) - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.6': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@iconify-json/carbon@1.2.9': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/devicon-plain@1.2.31': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/devicon@1.2.32': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/logos@1.2.4': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/ri@1.2.5': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/simple-icons@1.2.39': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify-json/vscode-icons@1.2.23': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify/types@2.0.0': {} - - '@iconify/utils@2.3.0': - dependencies: - '@antfu/install-pkg': 1.1.0 - '@antfu/utils': 8.1.1 - '@iconify/types': 2.0.0 - debug: 4.4.1(supports-color@5.5.0) - globals: 15.15.0 - kolorist: 1.8.0 - local-pkg: 1.1.1 - mlly: 1.7.4 - transitivePeerDependencies: - - supports-color - - '@intlify/bundle-utils@10.0.1(vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3)))': - dependencies: - '@intlify/message-compiler': 11.1.10 - '@intlify/shared': 11.1.10 - acorn: 8.15.0 - escodegen: 2.1.0 - estree-walker: 2.0.2 - jsonc-eslint-parser: 2.4.0 - mlly: 1.7.4 - source-map-js: 1.2.1 - yaml-eslint-parser: 1.3.0 - optionalDependencies: - vue-i18n: 11.1.10(vue@3.5.17(typescript@5.8.3)) - - '@intlify/core-base@11.1.10': - dependencies: - '@intlify/message-compiler': 11.1.10 - '@intlify/shared': 11.1.10 - - '@intlify/message-compiler@11.1.10': - dependencies: - '@intlify/shared': 11.1.10 - source-map-js: 1.2.1 - - '@intlify/shared@11.1.10': {} - - '@intlify/unplugin-vue-i18n@6.0.8(@vue/compiler-dom@3.5.17)(eslint@9.29.0(jiti@2.4.2))(rollup@4.45.1)(typescript@5.8.3)(vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2)) - '@intlify/bundle-utils': 10.0.1(vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3))) - '@intlify/shared': 11.1.10 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.10)(@vue/compiler-dom@3.5.17)(vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - debug: 4.4.1(supports-color@5.5.0) - fast-glob: 3.3.3 - js-yaml: 4.1.0 - json5: 2.2.3 - pathe: 1.1.2 - picocolors: 1.1.1 - source-map-js: 1.2.1 - unplugin: 1.16.1 - vue: 3.5.17(typescript@5.8.3) - optionalDependencies: - vue-i18n: 11.1.10(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - '@vue/compiler-dom' - - eslint - - rollup - - supports-color - - typescript - - '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.10)(@vue/compiler-dom@3.5.17)(vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@babel/parser': 7.28.0 - optionalDependencies: - '@intlify/shared': 11.1.10 - '@vue/compiler-dom': 3.5.17 - vue: 3.5.17(typescript@5.8.3) - vue-i18n: 11.1.10(vue@3.5.17(typescript@5.8.3)) - - '@jridgewell/gen-mapping@0.3.12': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/source-map@0.3.10': - dependencies: - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 - - '@jridgewell/sourcemap-codec@1.5.4': {} - - '@jridgewell/trace-mapping@0.3.29': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 - - '@kwsites/file-exists@1.1.1': - dependencies: - debug: 4.4.1(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - '@kwsites/promise-deferred@1.1.1': {} - - '@lit-labs/ssr-dom-shim@1.4.0': {} - - '@lit/reactive-element@2.1.1': - dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 - - '@mdit-vue/plugin-component@2.1.4': - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - - '@mdit-vue/plugin-frontmatter@2.1.4': - dependencies: - '@mdit-vue/types': 2.1.4 - '@types/markdown-it': 14.1.2 - gray-matter: 4.0.3 - markdown-it: 14.1.0 - - '@mdit-vue/types@2.1.4': {} - - '@mermaid-js/parser@0.6.2': - dependencies: - langium: 3.3.1 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@octokit/auth-token@5.1.2': {} - - '@octokit/core@6.1.6': - dependencies: - '@octokit/auth-token': 5.1.2 - '@octokit/graphql': 8.2.2 - '@octokit/request': 9.2.4 - '@octokit/request-error': 6.1.8 - '@octokit/types': 14.1.0 - before-after-hook: 3.0.2 - universal-user-agent: 7.0.3 - - '@octokit/endpoint@10.1.4': - dependencies: - '@octokit/types': 14.1.0 - universal-user-agent: 7.0.3 - - '@octokit/graphql@8.2.2': - dependencies: - '@octokit/request': 9.2.4 - '@octokit/types': 14.1.0 - universal-user-agent: 7.0.3 - - '@octokit/openapi-types@24.2.0': {} - - '@octokit/openapi-types@25.1.0': {} - - '@octokit/plugin-paginate-rest@11.6.0(@octokit/core@6.1.6)': - dependencies: - '@octokit/core': 6.1.6 - '@octokit/types': 13.10.0 - - '@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.6)': - dependencies: - '@octokit/core': 6.1.6 - - '@octokit/plugin-rest-endpoint-methods@13.5.0(@octokit/core@6.1.6)': - dependencies: - '@octokit/core': 6.1.6 - '@octokit/types': 13.10.0 - - '@octokit/request-error@6.1.8': - dependencies: - '@octokit/types': 14.1.0 - - '@octokit/request@9.2.4': - dependencies: - '@octokit/endpoint': 10.1.4 - '@octokit/request-error': 6.1.8 - '@octokit/types': 14.1.0 - fast-content-type-parse: 2.0.1 - universal-user-agent: 7.0.3 - - '@octokit/rest@21.1.1': - dependencies: - '@octokit/core': 6.1.6 - '@octokit/plugin-paginate-rest': 11.6.0(@octokit/core@6.1.6) - '@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.6) - '@octokit/plugin-rest-endpoint-methods': 13.5.0(@octokit/core@6.1.6) - - '@octokit/types@13.10.0': - dependencies: - '@octokit/openapi-types': 24.2.0 - - '@octokit/types@14.1.0': - dependencies: - '@octokit/openapi-types': 25.1.0 - - '@parcel/watcher-android-arm64@2.5.1': - optional: true - - '@parcel/watcher-darwin-arm64@2.5.1': - optional: true - - '@parcel/watcher-darwin-x64@2.5.1': - optional: true - - '@parcel/watcher-freebsd-x64@2.5.1': - optional: true - - '@parcel/watcher-linux-arm-glibc@2.5.1': - optional: true - - '@parcel/watcher-linux-arm-musl@2.5.1': - optional: true - - '@parcel/watcher-linux-arm64-glibc@2.5.1': - optional: true - - '@parcel/watcher-linux-arm64-musl@2.5.1': - optional: true - - '@parcel/watcher-linux-x64-glibc@2.5.1': - optional: true - - '@parcel/watcher-linux-x64-musl@2.5.1': - optional: true - - '@parcel/watcher-win32-arm64@2.5.1': - optional: true - - '@parcel/watcher-win32-ia32@2.5.1': - optional: true - - '@parcel/watcher-win32-x64@2.5.1': - optional: true - - '@parcel/watcher@2.5.1': - dependencies: - detect-libc: 1.0.3 - is-glob: 4.0.3 - micromatch: 4.0.8 - node-addon-api: 7.1.1 - optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.1 - '@parcel/watcher-darwin-arm64': 2.5.1 - '@parcel/watcher-darwin-x64': 2.5.1 - '@parcel/watcher-freebsd-x64': 2.5.1 - '@parcel/watcher-linux-arm-glibc': 2.5.1 - '@parcel/watcher-linux-arm-musl': 2.5.1 - '@parcel/watcher-linux-arm64-glibc': 2.5.1 - '@parcel/watcher-linux-arm64-musl': 2.5.1 - '@parcel/watcher-linux-x64-glibc': 2.5.1 - '@parcel/watcher-linux-x64-musl': 2.5.1 - '@parcel/watcher-win32-arm64': 2.5.1 - '@parcel/watcher-win32-ia32': 2.5.1 - '@parcel/watcher-win32-x64': 2.5.1 - optional: true - - '@polka/url@1.0.0-next.29': {} - - '@quansync/fs@0.1.3': - dependencies: - quansync: 0.2.10 - - '@rolldown/pluginutils@1.0.0-beta.19': {} - - '@rollup/pluginutils@5.2.0(rollup@4.45.1)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.45.1 - - '@rollup/rollup-android-arm-eabi@4.45.1': - optional: true - - '@rollup/rollup-android-arm64@4.45.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.45.1': - optional: true - - '@rollup/rollup-darwin-x64@4.45.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.45.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.45.1': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.45.1': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.45.1': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.45.1': - optional: true - - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.45.1': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.45.1': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.45.1': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.45.1': - optional: true - - '@rollup/rollup-linux-x64-musl@4.45.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.45.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.45.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.45.1': - optional: true - - '@sec-ant/readable-stream@0.4.1': {} - - '@selderee/plugin-htmlparser2@0.11.0': - dependencies: - domhandler: 5.0.3 - selderee: 0.11.0 - - '@shikijs/core@1.29.2': - dependencies: - '@shikijs/engine-javascript': 1.29.2 - '@shikijs/engine-oniguruma': 1.29.2 - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/core@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 2.3.0 - - '@shikijs/engine-javascript@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.3 - - '@shikijs/engine-oniguruma@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/engine-oniguruma@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - - '@shikijs/langs@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 - - '@shikijs/themes@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - - '@shikijs/themes@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 - - '@shikijs/transformers@1.29.2': - dependencies: - '@shikijs/core': 1.29.2 - '@shikijs/types': 1.29.2 - - '@shikijs/transformers@3.8.1': - dependencies: - '@shikijs/core': 3.8.1 - '@shikijs/types': 3.8.1 - - '@shikijs/types@1.29.2': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/types@3.8.1': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@sindresorhus/merge-streams@4.0.0': {} - - '@types/d3-array@3.2.1': {} - - '@types/d3-axis@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-brush@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-chord@3.0.6': {} - - '@types/d3-color@3.1.3': {} - - '@types/d3-contour@3.0.6': - dependencies: - '@types/d3-array': 3.2.1 - '@types/geojson': 7946.0.16 - - '@types/d3-delaunay@6.0.4': {} - - '@types/d3-dispatch@3.0.6': {} - - '@types/d3-drag@3.0.7': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-dsv@3.0.7': {} - - '@types/d3-ease@3.0.2': {} - - '@types/d3-fetch@3.0.7': - dependencies: - '@types/d3-dsv': 3.0.7 - - '@types/d3-force@3.0.10': {} - - '@types/d3-format@3.0.4': {} - - '@types/d3-geo@3.1.0': - dependencies: - '@types/geojson': 7946.0.16 - - '@types/d3-hierarchy@3.1.7': {} - - '@types/d3-interpolate@3.0.4': - dependencies: - '@types/d3-color': 3.1.3 - - '@types/d3-path@3.1.1': {} - - '@types/d3-polygon@3.0.2': {} - - '@types/d3-quadtree@3.0.6': {} - - '@types/d3-random@3.0.3': {} - - '@types/d3-scale-chromatic@3.1.0': {} - - '@types/d3-scale@4.0.9': - dependencies: - '@types/d3-time': 3.0.4 - - '@types/d3-selection@3.0.11': {} - - '@types/d3-shape@3.1.7': - dependencies: - '@types/d3-path': 3.1.1 - - '@types/d3-time-format@4.0.3': {} - - '@types/d3-time@3.0.4': {} - - '@types/d3-timer@3.0.2': {} - - '@types/d3-transition@3.0.9': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-zoom@3.0.8': - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - - '@types/d3@7.4.3': - dependencies: - '@types/d3-array': 3.2.1 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.6 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.10 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.1.1 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 - '@types/d3-scale': 4.0.9 - '@types/d3-scale-chromatic': 3.1.0 - '@types/d3-selection': 3.0.11 - '@types/d3-shape': 3.1.7 - '@types/d3-time': 3.0.4 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - - '@types/estree@1.0.8': {} - - '@types/geojson@7946.0.16': {} - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/http-proxy@1.17.16': - dependencies: - '@types/node': 24.0.15 - - '@types/json-schema@7.0.15': {} - - '@types/katex@0.16.7': {} - - '@types/linkify-it@5.0.0': {} - - '@types/markdown-it@14.1.2': - dependencies: - '@types/linkify-it': 5.0.0 - '@types/mdurl': 2.0.0 - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdurl@2.0.0': {} - - '@types/node@24.0.15': - dependencies: - undici-types: 7.8.0 - - '@types/trusted-types@2.0.7': {} - - '@types/unist@3.0.3': {} - - '@types/web-bluetooth@0.0.20': {} - - '@types/web-bluetooth@0.0.21': {} - - '@typescript-eslint/project-service@8.37.0(typescript@5.8.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) - '@typescript-eslint/types': 8.37.0 - debug: 4.4.1(supports-color@5.5.0) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.37.0': - dependencies: - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/visitor-keys': 8.37.0 - - '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.3)': - dependencies: - typescript: 5.8.3 - - '@typescript-eslint/types@8.37.0': {} - - '@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.3)': - dependencies: - '@typescript-eslint/project-service': 8.37.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/visitor-keys': 8.37.0 - debug: 4.4.1(supports-color@5.5.0) - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.37.0': - dependencies: - '@typescript-eslint/types': 8.37.0 - eslint-visitor-keys: 4.2.1 - - '@ungap/structured-clone@1.3.0': {} - - '@unhead/addons@2.0.12(rollup@4.45.1)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - estree-walker: 3.0.3 - magic-string: 0.30.17 - mlly: 1.7.4 - ufo: 1.6.1 - unplugin: 2.3.5 - unplugin-ast: 0.15.1 - transitivePeerDependencies: - - rollup - - '@unhead/dom@2.0.12(unhead@2.0.12)': - dependencies: - unhead: 2.0.12 - - '@unhead/schema-org@2.0.12(@unhead/vue@2.0.12(vue@3.5.17(typescript@5.8.3)))': - dependencies: - defu: 6.1.4 - ohash: 2.0.11 - ufo: 1.6.1 - unhead: 2.0.12 - optionalDependencies: - '@unhead/vue': 2.0.12(vue@3.5.17(typescript@5.8.3)) - - '@unhead/vue@2.0.12(vue@3.5.17(typescript@5.8.3))': - dependencies: - hookable: 5.5.3 - unhead: 2.0.12 - vue: 3.5.17(typescript@5.8.3) - - '@unocss/astro@66.3.3(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/reset': 66.3.3 - '@unocss/vite': 66.3.3(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - optionalDependencies: - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - vue - - '@unocss/cli@66.3.3': - dependencies: - '@ampproject/remapping': 2.3.0 - '@unocss/config': 66.3.3 - '@unocss/core': 66.3.3 - '@unocss/preset-uno': 66.3.3 - cac: 6.7.14 - chokidar: 3.6.0 - colorette: 2.0.20 - consola: 3.4.2 - magic-string: 0.30.17 - pathe: 2.0.3 - perfect-debounce: 1.0.0 - tinyglobby: 0.2.14 - unplugin-utils: 0.2.4 - - '@unocss/config@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - unconfig: 7.3.2 - - '@unocss/core@66.3.3': {} - - '@unocss/extractor-arbitrary-variants@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - - '@unocss/inspector@66.3.3(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/rule-utils': 66.3.3 - colorette: 2.0.20 - gzip-size: 6.0.0 - sirv: 3.0.1 - vue-flow-layout: 0.1.1(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - vue - - '@unocss/postcss@66.3.3(postcss@8.5.6)': - dependencies: - '@unocss/config': 66.3.3 - '@unocss/core': 66.3.3 - '@unocss/rule-utils': 66.3.3 - css-tree: 3.1.0 - postcss: 8.5.6 - tinyglobby: 0.2.14 - - '@unocss/preset-attributify@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - - '@unocss/preset-icons@66.3.3': - dependencies: - '@iconify/utils': 2.3.0 - '@unocss/core': 66.3.3 - ofetch: 1.4.1 - transitivePeerDependencies: - - supports-color - - '@unocss/preset-mini@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/extractor-arbitrary-variants': 66.3.3 - '@unocss/rule-utils': 66.3.3 - - '@unocss/preset-tagify@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - - '@unocss/preset-typography@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/preset-mini': 66.3.3 - '@unocss/rule-utils': 66.3.3 - - '@unocss/preset-uno@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/preset-wind3': 66.3.3 - - '@unocss/preset-web-fonts@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - ofetch: 1.4.1 - - '@unocss/preset-wind3@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/preset-mini': 66.3.3 - '@unocss/rule-utils': 66.3.3 - - '@unocss/preset-wind4@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/extractor-arbitrary-variants': 66.3.3 - '@unocss/rule-utils': 66.3.3 - - '@unocss/preset-wind@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/preset-wind3': 66.3.3 - - '@unocss/reset@66.3.3': {} - - '@unocss/rule-utils@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - magic-string: 0.30.17 - - '@unocss/transformer-attributify-jsx@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - - '@unocss/transformer-compile-class@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - - '@unocss/transformer-directives@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - '@unocss/rule-utils': 66.3.3 - css-tree: 3.1.0 - - '@unocss/transformer-variant-group@66.3.3': - dependencies: - '@unocss/core': 66.3.3 - - '@unocss/vite@66.3.3(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@ampproject/remapping': 2.3.0 - '@unocss/config': 66.3.3 - '@unocss/core': 66.3.3 - '@unocss/inspector': 66.3.3(vue@3.5.17(typescript@5.8.3)) - chokidar: 3.6.0 - magic-string: 0.30.17 - pathe: 2.0.3 - tinyglobby: 0.2.14 - unplugin-utils: 0.2.4 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - vue - - '@valaxyjs/devtools@0.25.9(debug@4.4.1)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - axios: 1.10.0(debug@4.4.1) - body-parser: 2.2.0 - cors: 2.8.5 - http-proxy-middleware: 3.0.5 - js-yaml: 4.1.0 - pathe: 2.0.3 - sirv: 3.0.1 - vite-dev-rpc: 1.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - transitivePeerDependencies: - - debug - - rollup - - supports-color - - vite - - '@valaxyjs/utils@0.25.9': {} - - '@vitejs/plugin-vue@5.2.4(vite@5.4.19(@types/node@24.0.15)(sass@1.89.2)(terser@5.43.1))(vue@3.5.17(typescript@5.8.3))': - dependencies: - vite: 5.4.19(@types/node@24.0.15)(sass@1.89.2)(terser@5.43.1) - vue: 3.5.17(typescript@5.8.3) - - '@vitejs/plugin-vue@6.0.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@rolldown/pluginutils': 1.0.0-beta.19 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vue: 3.5.17(typescript@5.8.3) - - '@volar/language-core@2.4.15': - dependencies: - '@volar/source-map': 2.4.15 - - '@volar/source-map@2.4.15': {} - - '@volar/typescript@2.4.15': - dependencies: - '@volar/language-core': 2.4.15 - path-browserify: 1.0.1 - vscode-uri: 3.1.0 - - '@vue-macros/common@3.0.0-beta.15(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@vue/compiler-sfc': 3.5.17 - ast-kit: 2.1.1 - local-pkg: 1.1.1 - magic-string-ast: 1.0.0 - unplugin-utils: 0.2.4 - optionalDependencies: - vue: 3.5.17(typescript@5.8.3) - - '@vue/babel-helper-vue-transform-on@1.4.0': {} - - '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.28.0)': - dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.1 - '@vue/babel-helper-vue-transform-on': 1.4.0 - '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.0) - '@vue/shared': 3.5.17 - optionalDependencies: - '@babel/core': 7.28.0 - transitivePeerDependencies: - - supports-color - - '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.28.0)': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.28.0 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.28.0 - '@vue/compiler-sfc': 3.5.17 - transitivePeerDependencies: - - supports-color - - '@vue/compiler-core@3.5.17': - dependencies: - '@babel/parser': 7.28.0 - '@vue/shared': 3.5.17 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.5.17': - dependencies: - '@vue/compiler-core': 3.5.17 - '@vue/shared': 3.5.17 - - '@vue/compiler-sfc@3.5.17': - dependencies: - '@babel/parser': 7.28.0 - '@vue/compiler-core': 3.5.17 - '@vue/compiler-dom': 3.5.17 - '@vue/compiler-ssr': 3.5.17 - '@vue/shared': 3.5.17 - estree-walker: 2.0.2 - magic-string: 0.30.17 - postcss: 8.5.6 - source-map-js: 1.2.1 - - '@vue/compiler-ssr@3.5.17': - dependencies: - '@vue/compiler-dom': 3.5.17 - '@vue/shared': 3.5.17 - - '@vue/compiler-vue2@2.7.16': - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - '@vue/devtools-api@6.6.4': {} - - '@vue/devtools-api@7.7.2': - dependencies: - '@vue/devtools-kit': 7.7.7 - - '@vue/devtools-api@7.7.7': - dependencies: - '@vue/devtools-kit': 7.7.7 - - '@vue/devtools-core@7.7.7(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@vue/devtools-kit': 7.7.7 - '@vue/devtools-shared': 7.7.7 - mitt: 3.0.1 - nanoid: 5.1.5 - pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - vue: 3.5.17(typescript@5.8.3) - transitivePeerDependencies: - - vite - - '@vue/devtools-kit@7.7.7': - dependencies: - '@vue/devtools-shared': 7.7.7 - birpc: 2.5.0 - hookable: 5.5.3 - mitt: 3.0.1 - perfect-debounce: 1.0.0 - speakingurl: 14.0.1 - superjson: 2.2.2 - - '@vue/devtools-shared@7.7.7': - dependencies: - rfdc: 1.4.1 - - '@vue/language-core@2.2.12(typescript@5.8.3)': - dependencies: - '@volar/language-core': 2.4.15 - '@vue/compiler-dom': 3.5.17 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.17 - alien-signals: 1.0.13 - minimatch: 9.0.5 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.8.3 - - '@vue/reactivity@3.5.17': - dependencies: - '@vue/shared': 3.5.17 - - '@vue/runtime-core@3.5.17': - dependencies: - '@vue/reactivity': 3.5.17 - '@vue/shared': 3.5.17 - - '@vue/runtime-dom@3.5.17': - dependencies: - '@vue/reactivity': 3.5.17 - '@vue/runtime-core': 3.5.17 - '@vue/shared': 3.5.17 - csstype: 3.1.3 - - '@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@vue/compiler-ssr': 3.5.17 - '@vue/shared': 3.5.17 - vue: 3.5.17(typescript@5.8.3) - - '@vue/shared@3.5.17': {} - - '@vueuse/core@10.11.1(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.11.1 - '@vueuse/shared': 10.11.1(vue@3.5.17(typescript@5.8.3)) - vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - '@vueuse/core@13.5.0(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@types/web-bluetooth': 0.0.21 - '@vueuse/metadata': 13.5.0 - '@vueuse/shared': 13.5.0(vue@3.5.17(typescript@5.8.3)) - vue: 3.5.17(typescript@5.8.3) - - '@vueuse/integrations@10.11.1(axios@1.10.0)(focus-trap@7.6.5)(fuse.js@7.1.0)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@vueuse/core': 10.11.1(vue@3.5.17(typescript@5.8.3)) - '@vueuse/shared': 10.11.1(vue@3.5.17(typescript@5.8.3)) - vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3)) - optionalDependencies: - axios: 1.10.0(debug@4.4.1) - focus-trap: 7.6.5 - fuse.js: 7.1.0 - nprogress: 0.2.0 - qrcode: 1.5.4 - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - '@vueuse/integrations@13.5.0(axios@1.10.0)(focus-trap@7.6.5)(fuse.js@7.1.0)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.17(typescript@5.8.3))': - dependencies: - '@vueuse/core': 13.5.0(vue@3.5.17(typescript@5.8.3)) - '@vueuse/shared': 13.5.0(vue@3.5.17(typescript@5.8.3)) - vue: 3.5.17(typescript@5.8.3) - optionalDependencies: - axios: 1.10.0(debug@4.4.1) - focus-trap: 7.6.5 - fuse.js: 7.1.0 - nprogress: 0.2.0 - qrcode: 1.5.4 - - '@vueuse/metadata@10.11.1': {} - - '@vueuse/metadata@13.5.0': {} - - '@vueuse/shared@10.11.1(vue@3.5.17(typescript@5.8.3))': - dependencies: - vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - '@vueuse/shared@13.5.0(vue@3.5.17(typescript@5.8.3))': - dependencies: - vue: 3.5.17(typescript@5.8.3) - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn@8.15.0: {} - - agent-base@7.1.4: {} - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - algoliasearch@5.34.0: - dependencies: - '@algolia/client-abtesting': 5.34.0 - '@algolia/client-analytics': 5.34.0 - '@algolia/client-common': 5.34.0 - '@algolia/client-insights': 5.34.0 - '@algolia/client-personalization': 5.34.0 - '@algolia/client-query-suggestions': 5.34.0 - '@algolia/client-search': 5.34.0 - '@algolia/ingestion': 1.34.0 - '@algolia/monitoring': 1.34.0 - '@algolia/recommend': 5.34.0 - '@algolia/requester-browser-xhr': 5.34.0 - '@algolia/requester-fetch': 5.34.0 - '@algolia/requester-node-http': 5.34.0 - - alien-signals@1.0.13: {} - - ansi-escapes@7.0.0: - dependencies: - environment: 1.1.0 - - ansi-regex@5.0.1: {} - - ansi-regex@6.1.0: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.1: {} - - ansis@4.1.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - ast-kit@2.1.1: - dependencies: - '@babel/parser': 7.28.0 - pathe: 2.0.3 - - ast-walker-scope@0.8.1: - dependencies: - '@babel/parser': 7.28.0 - ast-kit: 2.1.1 - - astral-regex@2.0.0: {} - - async@3.2.6: {} - - asynckit@0.4.0: {} - - axios@1.10.0(debug@4.4.1): - dependencies: - follow-redirects: 1.15.9(debug@4.4.1) - form-data: 4.0.4 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - balanced-match@1.0.2: {} - - beasties@0.3.5: - dependencies: - css-select: 6.0.0 - css-what: 7.0.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - htmlparser2: 10.0.0 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-media-query-parser: 0.2.3 - - before-after-hook@3.0.2: {} - - binary-extensions@2.3.0: {} - - birpc@2.5.0: {} - - blueimp-md5@2.19.0: {} - - body-parser@2.2.0: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.1(supports-color@5.5.0) - http-errors: 2.0.0 - iconv-lite: 0.6.3 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.0 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - - boolbase@1.0.0: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.25.1: - dependencies: - caniuse-lite: 1.0.30001727 - electron-to-chromium: 1.5.187 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.1) - - buffer-from@1.1.2: {} - - bundle-name@4.1.0: - dependencies: - run-applescript: 7.0.0 - - bytes@3.1.2: {} - - cac@6.7.14: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - camel-case@4.1.2: - dependencies: - pascal-case: 3.1.2 - tslib: 2.8.1 - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001727: {} - - ccount@2.0.1: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@5.4.1: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - charenc@0.0.2: {} - - chevrotain-allstar@0.3.1(chevrotain@11.0.3): - dependencies: - chevrotain: 11.0.3 - lodash-es: 4.17.21 - - chevrotain@11.0.3: - dependencies: - '@chevrotain/cst-dts-gen': 11.0.3 - '@chevrotain/gast': 11.0.3 - '@chevrotain/regexp-to-ast': 11.0.3 - '@chevrotain/types': 11.0.3 - '@chevrotain/utils': 11.0.3 - lodash-es: 4.17.21 - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - clean-css@5.3.3: - dependencies: - source-map: 0.6.1 - - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-spinners@2.9.2: {} - - cli-truncate@4.0.0: - dependencies: - slice-ansi: 5.0.0 - string-width: 7.2.0 - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - - cliui@9.0.1: - dependencies: - string-width: 7.2.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - colorette@2.0.20: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@2.0.3: {} - - commander@10.0.1: {} - - commander@14.0.0: {} - - commander@2.20.3: {} - - commander@7.2.0: {} - - commander@8.3.0: {} - - concat-map@0.0.1: {} - - confbox@0.1.8: {} - - confbox@0.2.2: {} - - consola@3.4.2: {} - - content-type@1.0.5: {} - - convert-source-map@2.0.0: {} - - copy-anything@3.0.5: - dependencies: - is-what: 4.1.16 - - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cose-base@1.0.3: - dependencies: - layout-base: 1.0.2 - - cose-base@2.2.0: - dependencies: - layout-base: 2.0.1 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - crypt@0.0.2: {} - - css-i18n@0.0.5: {} - - css-select@6.0.0: - dependencies: - boolbase: 1.0.0 - css-what: 7.0.0 - domhandler: 5.0.3 - domutils: 3.2.2 - nth-check: 2.1.1 - - css-tree@3.1.0: - dependencies: - mdn-data: 2.12.2 - source-map-js: 1.2.1 - - css-what@7.0.0: {} - - cssstyle@4.6.0: - dependencies: - '@asamuzakjp/css-color': 3.2.0 - rrweb-cssom: 0.8.0 - - csstype@3.1.3: {} - - cytoscape-cose-bilkent@4.1.0(cytoscape@3.32.1): - dependencies: - cose-base: 1.0.3 - cytoscape: 3.32.1 - - cytoscape-fcose@2.2.0(cytoscape@3.32.1): - dependencies: - cose-base: 2.2.0 - cytoscape: 3.32.1 - - cytoscape@3.32.1: {} - - d3-array@2.12.1: - dependencies: - internmap: 1.0.1 - - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-axis@3.0.0: {} - - d3-brush@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3-chord@3.0.1: - dependencies: - d3-path: 3.1.0 - - d3-color@3.1.0: {} - - d3-contour@4.0.2: - dependencies: - d3-array: 3.2.4 - - d3-delaunay@6.0.4: - dependencies: - delaunator: 5.0.1 - - d3-dispatch@3.0.1: {} - - d3-drag@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-selection: 3.0.0 - - d3-dsv@3.0.1: - dependencies: - commander: 7.2.0 - iconv-lite: 0.6.3 - rw: 1.3.3 - - d3-ease@3.0.1: {} - - d3-fetch@3.0.1: - dependencies: - d3-dsv: 3.0.1 - - d3-force@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - - d3-format@3.1.0: {} - - d3-geo@3.1.1: - dependencies: - d3-array: 3.2.4 - - d3-hierarchy@3.1.2: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@1.0.9: {} - - d3-path@3.1.0: {} - - d3-polygon@3.0.1: {} - - d3-quadtree@3.0.1: {} - - d3-random@3.0.1: {} - - d3-sankey@0.12.3: - dependencies: - d3-array: 2.12.1 - d3-shape: 1.3.7 - - d3-scale-chromatic@3.1.0: - dependencies: - d3-color: 3.1.0 - d3-interpolate: 3.0.1 - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.0 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-selection@3.0.0: {} - - d3-shape@1.3.7: - dependencies: - d3-path: 1.0.9 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - d3-transition@3.0.1(d3-selection@3.0.0): - dependencies: - d3-color: 3.1.0 - d3-dispatch: 3.0.1 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-timer: 3.0.1 - - d3-zoom@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3@7.9.0: - dependencies: - d3-array: 3.2.4 - d3-axis: 3.0.0 - d3-brush: 3.0.0 - d3-chord: 3.0.1 - d3-color: 3.1.0 - d3-contour: 4.0.2 - d3-delaunay: 6.0.4 - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-dsv: 3.0.1 - d3-ease: 3.0.1 - d3-fetch: 3.0.1 - d3-force: 3.0.0 - d3-format: 3.1.0 - d3-geo: 3.1.1 - d3-hierarchy: 3.1.2 - d3-interpolate: 3.0.1 - d3-path: 3.1.0 - d3-polygon: 3.0.1 - d3-quadtree: 3.0.1 - d3-random: 3.0.1 - d3-scale: 4.0.2 - d3-scale-chromatic: 3.1.0 - d3-selection: 3.0.0 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - d3-timer: 3.0.1 - d3-transition: 3.0.1(d3-selection@3.0.0) - d3-zoom: 3.0.0 - - dagre-d3-es@7.0.11: - dependencies: - d3: 7.9.0 - lodash-es: 4.17.21 - - data-urls@5.0.0: - dependencies: - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - - dayjs@1.11.13: {} - - de-indent@1.0.2: {} - - debug@4.4.1(supports-color@5.5.0): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 5.5.0 - - decamelize@1.2.0: {} - - decimal.js@10.6.0: {} - - deep-is@0.1.4: {} - - deepmerge@4.3.1: {} - - default-browser-id@5.0.0: {} - - default-browser@5.2.1: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.0 - - define-lazy-prop@3.0.0: {} - - defu@6.1.4: {} - - delaunator@5.0.1: - dependencies: - robust-predicates: 3.0.2 - - delayed-stream@1.0.0: {} - - depd@2.0.0: {} - - dequal@2.0.3: {} - - destr@2.0.5: {} - - detect-libc@1.0.3: - optional: true - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - dijkstrajs@1.0.3: {} - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - dompurify@3.2.6: - optionalDependencies: - '@types/trusted-types': 2.0.7 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - - dot-case@3.0.4: - dependencies: - no-case: 3.0.4 - tslib: 2.8.1 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - duplexer@0.1.2: {} - - ee-first@1.1.1: {} - - ejs@3.1.10: - dependencies: - jake: 10.9.2 - - electron-to-chromium@1.5.187: {} - - email-validator@2.0.4: {} - - emoji-regex-xs@1.0.0: {} - - emoji-regex@10.4.0: {} - - emoji-regex@8.0.0: {} - - entities@4.5.0: {} - - entities@6.0.1: {} - - environment@1.1.0: {} - - error-stack-parser-es@0.1.5: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.25.8: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 - - escalade@3.2.0: {} - - escape-html@1.0.3: {} - - escape-string-regexp@4.0.0: {} - - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint@9.29.0(jiti@2.4.2): - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.1 - '@eslint/config-helpers': 0.2.3 - '@eslint/core': 0.14.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.29.0 - '@eslint/plugin-kit': 0.3.3 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.1(supports-color@5.5.0) - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.4.2 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 - - espree@9.6.1: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - estree-walker@2.0.2: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - - esutils@2.0.3: {} - - eventemitter3@4.0.7: {} - - eventemitter3@5.0.1: {} - - execa@9.6.0: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.1 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.2.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.1.1 - - exsolve@1.0.7: {} - - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - - fast-content-type-parse@2.0.1: {} - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fast-uri@3.0.6: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fdir@6.4.6(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - feed@5.1.0: - dependencies: - xml-js: 1.6.11 - - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - - flatted@3.3.3: {} - - floating-vue@5.2.2(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@floating-ui/dom': 1.1.1 - vue: 3.5.17(typescript@5.8.3) - vue-resize: 2.0.0-alpha.1(vue@3.5.17(typescript@5.8.3)) - - focus-trap@7.6.5: - dependencies: - tabbable: 6.2.0 - - follow-redirects@1.15.9(debug@4.4.1): - optionalDependencies: - debug: 4.4.1(supports-color@5.5.0) - - form-data@4.0.4: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - fs-extra@11.3.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - fuse.js@7.1.0: {} - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-east-asian-width@1.3.0: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 - - giscus@1.6.0: - dependencies: - lit: 3.3.1 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - global-directory@4.0.1: - dependencies: - ini: 4.1.1 - - globals@14.0.0: {} - - globals@15.15.0: {} - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - gravatar@1.8.2: - dependencies: - blueimp-md5: 2.19.0 - email-validator: 2.0.4 - querystring: 0.2.0 - yargs: 15.4.1 - - gray-matter@4.0.3: - dependencies: - js-yaml: 3.14.1 - kind-of: 6.0.3 - section-matter: 1.0.0 - strip-bom-string: 1.0.0 - - gzip-size@6.0.0: - dependencies: - duplexer: 0.1.2 - - hachure-fill@0.5.2: {} - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.0 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - he@1.2.0: {} - - hookable@5.5.3: {} - - html-encoding-sniffer@4.0.0: - dependencies: - whatwg-encoding: 3.1.1 - - html-minifier-terser@7.2.0: - dependencies: - camel-case: 4.1.2 - clean-css: 5.3.3 - commander: 10.0.1 - entities: 4.5.0 - param-case: 3.0.4 - relateurl: 0.2.7 - terser: 5.43.1 - - html-to-text@9.0.5: - dependencies: - '@selderee/plugin-htmlparser2': 0.11.0 - deepmerge: 4.3.1 - dom-serializer: 2.0.0 - htmlparser2: 8.0.2 - selderee: 0.11.0 - - html-void-elements@3.0.0: {} - - html5parser@2.0.2: - dependencies: - tslib: 2.8.1 - - htmlparser2@10.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 6.0.1 - - htmlparser2@8.0.2: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 4.5.0 - - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - http-proxy-middleware@3.0.5: - dependencies: - '@types/http-proxy': 1.17.16 - debug: 4.4.1(supports-color@5.5.0) - http-proxy: 1.18.1(debug@4.4.1) - is-glob: 4.0.3 - is-plain-object: 5.0.0 - micromatch: 4.0.8 - transitivePeerDependencies: - - supports-color - - http-proxy@1.18.1(debug@4.4.1): - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.9(debug@4.4.1) - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - human-signals@8.0.1: {} - - husky@9.1.7: {} - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ignore-by-default@1.0.1: {} - - ignore@5.3.2: {} - - immutable@5.1.3: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - inherits@2.0.4: {} - - ini@4.1.1: {} - - internmap@1.0.1: {} - - internmap@2.0.3: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-buffer@1.1.6: {} - - is-docker@3.0.0: {} - - is-extendable@0.1.1: {} - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-fullwidth-code-point@4.0.0: {} - - is-fullwidth-code-point@5.0.0: - dependencies: - get-east-asian-width: 1.3.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-interactive@2.0.0: {} - - is-number@7.0.0: {} - - is-plain-obj@4.1.0: {} - - is-plain-object@5.0.0: {} - - is-potential-custom-element-name@1.0.1: {} - - is-stream@4.0.1: {} - - is-unicode-supported@1.3.0: {} - - is-unicode-supported@2.1.0: {} - - is-what@4.1.16: {} - - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - - isexe@2.0.0: {} - - jake@10.9.2: - dependencies: - async: 3.2.6 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - - jiti@2.4.2: {} - - js-base64@3.7.7: {} - - js-tokens@4.0.0: {} - - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - jsdom@26.1.0: - dependencies: - cssstyle: 4.6.0 - data-urls: 5.0.0 - decimal.js: 10.6.0 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.20 - parse5: 7.3.0 - rrweb-cssom: 0.8.0 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 5.1.2 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - ws: 8.18.3 - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@2.2.3: {} - - jsonc-eslint-parser@2.4.0: - dependencies: - acorn: 8.15.0 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - semver: 7.7.2 - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - katex@0.16.22: - dependencies: - commander: 8.3.0 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - khroma@2.1.0: {} - - kind-of@6.0.3: {} - - kolorist@1.8.0: {} - - langium@3.3.1: - dependencies: - chevrotain: 11.0.3 - chevrotain-allstar: 0.3.1(chevrotain@11.0.3) - vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.0.8 - - layout-base@1.0.2: {} - - layout-base@2.0.1: {} - - leac@0.6.0: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lilconfig@3.1.3: {} - - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - - lint-staged@16.1.2: - dependencies: - chalk: 5.4.1 - commander: 14.0.0 - debug: 4.4.1(supports-color@5.5.0) - lilconfig: 3.1.3 - listr2: 8.3.3 - micromatch: 4.0.8 - nano-spawn: 1.0.2 - pidtree: 0.6.0 - string-argv: 0.3.2 - yaml: 2.8.0 - transitivePeerDependencies: - - supports-color - - listr2@8.3.3: - dependencies: - cli-truncate: 4.0.0 - colorette: 2.0.20 - eventemitter3: 5.0.1 - log-update: 6.1.0 - rfdc: 1.4.1 - wrap-ansi: 9.0.0 - - lit-element@4.2.1: - dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 - '@lit/reactive-element': 2.1.1 - lit-html: 3.3.1 - - lit-html@3.3.1: - dependencies: - '@types/trusted-types': 2.0.7 - - lit@3.3.1: - dependencies: - '@lit/reactive-element': 2.1.1 - lit-element: 4.2.1 - lit-html: 3.3.1 - - local-pkg@0.5.1: - dependencies: - mlly: 1.7.4 - pkg-types: 1.3.1 - - local-pkg@1.1.1: - dependencies: - mlly: 1.7.4 - pkg-types: 2.2.0 - quansync: 0.2.10 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash-es@4.17.21: {} - - lodash.merge@4.6.2: {} - - lodash.truncate@4.4.2: {} - - log-symbols@6.0.0: - dependencies: - chalk: 5.4.1 - is-unicode-supported: 1.3.0 - - log-update@6.1.0: - dependencies: - ansi-escapes: 7.0.0 - cli-cursor: 5.0.0 - slice-ansi: 7.1.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 - - lower-case@2.0.2: - dependencies: - tslib: 2.8.1 - - lru-cache@10.4.3: {} - - lru-cache@11.1.0: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - magic-string-ast@1.0.0: - dependencies: - magic-string: 0.30.17 - - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - - mark.js@8.11.1: {} - - markdown-it-anchor@9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0): - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - - markdown-it-async@2.2.0: - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - - markdown-it-attrs@4.3.1(markdown-it@14.1.0): - dependencies: - markdown-it: 14.1.0 - - markdown-it-container@4.0.0: {} - - markdown-it-emoji@3.0.0: {} - - markdown-it-footnote@4.0.0: {} - - markdown-it-image-figures@2.1.1(markdown-it@14.1.0): - dependencies: - markdown-it: 14.1.0 - - markdown-it-table-of-contents@0.9.0: {} - - markdown-it-task-lists@2.1.1: {} - - markdown-it@14.1.0: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - - marked@16.1.1: {} - - math-intrinsics@1.1.0: {} - - md5@2.3.0: - dependencies: - charenc: 0.0.2 - crypt: 0.0.2 - is-buffer: 1.1.6 - - mdast-util-to-hast@13.2.0: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - - mdn-data@2.12.2: {} - - mdurl@2.0.0: {} - - media-typer@1.1.0: {} - - medium-zoom@1.1.0: {} - - merge2@1.4.1: {} - - mermaid@11.9.0: - dependencies: - '@braintree/sanitize-url': 7.1.1 - '@iconify/utils': 2.3.0 - '@mermaid-js/parser': 0.6.2 - '@types/d3': 7.4.3 - cytoscape: 3.32.1 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.32.1) - cytoscape-fcose: 2.2.0(cytoscape@3.32.1) - d3: 7.9.0 - d3-sankey: 0.12.3 - dagre-d3-es: 7.0.11 - dayjs: 1.11.13 - dompurify: 3.2.6 - katex: 0.16.22 - khroma: 2.1.0 - lodash-es: 4.17.21 - marked: 16.1.1 - roughjs: 4.6.6 - stylis: 4.3.6 - ts-dedent: 2.2.0 - uuid: 11.1.0 - transitivePeerDependencies: - - supports-color - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-encode@2.0.1: {} - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: {} - - mime-db@1.54.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime-types@3.0.1: - dependencies: - mime-db: 1.54.0 - - mimic-function@5.0.1: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.2 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minisearch@6.3.0: {} - - mitt@3.0.1: {} - - mlly@1.7.4: - dependencies: - acorn: 8.15.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.1 - - mrmime@2.0.1: {} - - ms@2.1.3: {} - - muggle-string@0.4.1: {} - - nano-spawn@1.0.2: {} - - nanoid@3.3.11: {} - - nanoid@5.1.5: {} - - natural-compare@1.4.0: {} - - no-case@3.0.4: - dependencies: - lower-case: 2.0.2 - tslib: 2.8.1 - - node-addon-api@7.1.1: - optional: true - - node-fetch-native@1.6.6: {} - - node-releases@2.0.19: {} - - nodemon@3.1.10: - dependencies: - chokidar: 3.6.0 - debug: 4.4.1(supports-color@5.5.0) - ignore-by-default: 1.0.1 - minimatch: 3.1.2 - pstree.remy: 1.1.8 - semver: 7.7.2 - simple-update-notifier: 2.0.0 - supports-color: 5.5.0 - touch: 3.1.1 - undefsafe: 2.0.5 - - normalize-path@3.0.0: {} - - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - - nprogress@0.2.0: {} - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - nwsapi@2.2.20: {} - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - ofetch@1.4.1: - dependencies: - destr: 2.0.5 - node-fetch-native: 1.6.6 - ufo: 1.6.1 - - ohash@2.0.11: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - - oniguruma-parser@0.12.1: {} - - oniguruma-to-es@2.3.0: - dependencies: - emoji-regex-xs: 1.0.0 - regex: 5.1.1 - regex-recursion: 5.1.1 - - oniguruma-to-es@4.3.3: - dependencies: - oniguruma-parser: 0.12.1 - regex: 6.0.1 - regex-recursion: 6.0.2 - - open@10.1.0: - dependencies: - default-browser: 5.2.1 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 3.1.0 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - ora@8.2.0: - dependencies: - chalk: 5.4.1 - cli-cursor: 5.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-try@2.2.0: {} - - package-manager-detector@1.3.0: {} - - param-case@3.0.4: - dependencies: - dot-case: 3.0.4 - tslib: 2.8.1 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-ms@4.0.0: {} - - parse5@7.3.0: - dependencies: - entities: 6.0.1 - - parseley@0.12.1: - dependencies: - leac: 0.6.0 - peberminta: 0.9.0 - - pascal-case@3.1.2: - dependencies: - no-case: 3.0.4 - tslib: 2.8.1 - - pascalcase@2.0.0: - dependencies: - camelcase: 6.3.0 - - path-browserify@1.0.1: {} - - path-data-parser@0.1.0: {} - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - pathe@1.1.2: {} - - pathe@2.0.3: {} - - peberminta@0.9.0: {} - - perfect-debounce@1.0.0: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - pidtree@0.6.0: {} - - pinia@3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@vue/devtools-api': 7.7.2 - vue: 3.5.17(typescript@5.8.3) - optionalDependencies: - typescript: 5.8.3 - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.7.4 - pathe: 2.0.3 - - pkg-types@2.2.0: - dependencies: - confbox: 0.2.2 - exsolve: 1.0.7 - pathe: 2.0.3 - - pngjs@5.0.0: {} - - points-on-curve@0.2.0: {} - - points-on-path@0.2.1: - dependencies: - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - - postcss-media-query-parser@0.2.3: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - preact@10.26.9: {} - - prelude-ls@1.2.1: {} - - prettier@3.6.2: {} - - pretty-ms@9.2.0: - dependencies: - parse-ms: 4.0.0 - - property-information@7.1.0: {} - - proxy-from-env@1.1.0: {} - - pstree.remy@1.1.8: {} - - punycode.js@2.3.1: {} - - punycode@2.3.1: {} - - qrcode@1.5.4: - dependencies: - dijkstrajs: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 - - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - quansync@0.2.10: {} - - querystring@0.2.0: {} - - queue-microtask@1.2.3: {} - - raw-body@3.0.0: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - unpipe: 1.0.0 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - readdirp@4.1.2: {} - - regex-recursion@5.1.1: - dependencies: - regex: 5.1.1 - regex-utilities: 2.3.0 - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@5.1.1: - dependencies: - regex-utilities: 2.3.0 - - regex@6.0.1: - dependencies: - regex-utilities: 2.3.0 - - relateurl@0.2.7: {} - - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - - require-main-filename@2.0.0: {} - - requires-port@1.0.0: {} - - resolve-from@4.0.0: {} - - resolve-global@2.0.0: - dependencies: - global-directory: 4.0.1 - - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - - reusify@1.1.0: {} - - rfdc@1.4.1: {} - - robust-predicates@3.0.2: {} - - rollup@4.45.1: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.45.1 - '@rollup/rollup-android-arm64': 4.45.1 - '@rollup/rollup-darwin-arm64': 4.45.1 - '@rollup/rollup-darwin-x64': 4.45.1 - '@rollup/rollup-freebsd-arm64': 4.45.1 - '@rollup/rollup-freebsd-x64': 4.45.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 - '@rollup/rollup-linux-arm-musleabihf': 4.45.1 - '@rollup/rollup-linux-arm64-gnu': 4.45.1 - '@rollup/rollup-linux-arm64-musl': 4.45.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-musl': 4.45.1 - '@rollup/rollup-linux-s390x-gnu': 4.45.1 - '@rollup/rollup-linux-x64-gnu': 4.45.1 - '@rollup/rollup-linux-x64-musl': 4.45.1 - '@rollup/rollup-win32-arm64-msvc': 4.45.1 - '@rollup/rollup-win32-ia32-msvc': 4.45.1 - '@rollup/rollup-win32-x64-msvc': 4.45.1 - fsevents: 2.3.3 - - roughjs@4.6.6: - dependencies: - hachure-fill: 0.5.2 - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - points-on-path: 0.2.1 - - rrweb-cssom@0.8.0: {} - - run-applescript@7.0.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rw@1.3.3: {} - - safer-buffer@2.1.2: {} - - sass@1.89.2: - dependencies: - chokidar: 4.0.3 - immutable: 5.1.3 - source-map-js: 1.2.1 - optionalDependencies: - '@parcel/watcher': 2.5.1 - - sax@1.4.1: {} - - saxes@6.0.0: - dependencies: - xmlchars: 2.2.0 - - scule@1.3.0: {} - - search-insights@2.17.3: {} - - section-matter@1.0.0: - dependencies: - extend-shallow: 2.0.1 - kind-of: 6.0.3 - - selderee@0.11.0: - dependencies: - parseley: 0.12.1 - - semver@6.3.1: {} - - semver@7.7.2: {} - - set-blocking@2.0.0: {} - - setprototypeof@1.2.0: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shiki@1.29.2: - dependencies: - '@shikijs/core': 1.29.2 - '@shikijs/engine-javascript': 1.29.2 - '@shikijs/engine-oniguruma': 1.29.2 - '@shikijs/langs': 1.29.2 - '@shikijs/themes': 1.29.2 - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - shiki@3.8.1: - dependencies: - '@shikijs/core': 3.8.1 - '@shikijs/engine-javascript': 3.8.1 - '@shikijs/engine-oniguruma': 3.8.1 - '@shikijs/langs': 3.8.1 - '@shikijs/themes': 3.8.1 - '@shikijs/types': 3.8.1 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - signal-exit@4.1.0: {} - - simple-git@3.28.0: - dependencies: - '@kwsites/file-exists': 1.1.1 - '@kwsites/promise-deferred': 1.1.1 - debug: 4.4.1(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - simple-update-notifier@2.0.0: - dependencies: - semver: 7.7.2 - - sirv@3.0.1: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - - sisteransi@1.0.5: {} - - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - - slice-ansi@5.0.0: - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 - - slice-ansi@7.1.0: - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 5.0.0 - - source-map-js@1.2.1: {} - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - space-separated-tokens@2.0.2: {} - - speakingurl@14.0.1: {} - - sprintf-js@1.0.3: {} - - star-markdown-css@0.5.3: {} - - statuses@2.0.1: {} - - stdin-discarder@0.2.2: {} - - string-argv@0.3.2: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@7.2.0: - dependencies: - emoji-regex: 10.4.0 - get-east-asian-width: 1.3.0 - strip-ansi: 7.1.0 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.1.0 - - strip-bom-string@1.0.0: {} - - strip-final-newline@4.0.0: {} - - strip-json-comments@3.1.1: {} - - stylis@4.3.6: {} - - superjson@2.2.2: - dependencies: - copy-anything: 3.0.5 - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - symbol-tree@3.2.4: {} - - tabbable@6.2.0: {} - - table@6.9.0: - dependencies: - ajv: 8.17.1 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - terser@5.43.1: - dependencies: - '@jridgewell/source-map': 0.3.10 - acorn: 8.15.0 - commander: 2.20.3 - source-map-support: 0.5.21 - - tinyexec@1.0.1: {} - - tinyglobby@0.2.14: - dependencies: - fdir: 6.4.6(picomatch@4.0.3) - picomatch: 4.0.3 - - tldts-core@6.1.86: {} - - tldts@6.1.86: - dependencies: - tldts-core: 6.1.86 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toidentifier@1.0.1: {} - - totalist@3.0.1: {} - - touch@3.1.1: {} - - tough-cookie@5.1.2: - dependencies: - tldts: 6.1.86 - - tr46@5.1.1: - dependencies: - punycode: 2.3.1 - - trim-lines@3.0.1: {} - - ts-api-utils@2.1.0(typescript@5.8.3): - dependencies: - typescript: 5.8.3 - - ts-dedent@2.2.0: {} - - tslib@2.8.1: {} - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.1 - - typescript@5.8.3: {} - - uc.micro@2.1.0: {} - - ufo@1.6.1: {} - - unconfig@7.3.2: - dependencies: - '@quansync/fs': 0.1.3 - defu: 6.1.4 - jiti: 2.4.2 - quansync: 0.2.10 - - undefsafe@2.0.5: {} - - undici-types@7.8.0: {} - - unhead@2.0.12: - dependencies: - hookable: 5.5.3 - - unicorn-magic@0.3.0: {} - - unist-util-is@6.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.1: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - - universal-user-agent@7.0.3: {} - - universalify@2.0.1: {} - - unocss@66.3.3(postcss@8.5.6)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@unocss/astro': 66.3.3(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - '@unocss/cli': 66.3.3 - '@unocss/core': 66.3.3 - '@unocss/postcss': 66.3.3(postcss@8.5.6) - '@unocss/preset-attributify': 66.3.3 - '@unocss/preset-icons': 66.3.3 - '@unocss/preset-mini': 66.3.3 - '@unocss/preset-tagify': 66.3.3 - '@unocss/preset-typography': 66.3.3 - '@unocss/preset-uno': 66.3.3 - '@unocss/preset-web-fonts': 66.3.3 - '@unocss/preset-wind': 66.3.3 - '@unocss/preset-wind3': 66.3.3 - '@unocss/preset-wind4': 66.3.3 - '@unocss/transformer-attributify-jsx': 66.3.3 - '@unocss/transformer-compile-class': 66.3.3 - '@unocss/transformer-directives': 66.3.3 - '@unocss/transformer-variant-group': 66.3.3 - '@unocss/vite': 66.3.3(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - optionalDependencies: - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - postcss - - supports-color - - vue - - unpipe@1.0.0: {} - - unplugin-ast@0.15.1: - dependencies: - '@babel/generator': 7.28.0 - ast-kit: 2.1.1 - magic-string-ast: 1.0.0 - unplugin: 2.3.5 - - unplugin-utils@0.2.4: - dependencies: - pathe: 2.0.3 - picomatch: 4.0.3 - - unplugin-vue-components@28.0.0(@babel/parser@7.28.0)(rollup@4.45.1)(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - chokidar: 3.6.0 - debug: 4.4.1(supports-color@5.5.0) - fast-glob: 3.3.3 - local-pkg: 0.5.1 - magic-string: 0.30.17 - minimatch: 9.0.5 - mlly: 1.7.4 - unplugin: 2.3.5 - vue: 3.5.17(typescript@5.8.3) - optionalDependencies: - '@babel/parser': 7.28.0 - transitivePeerDependencies: - - rollup - - supports-color - - unplugin-vue-markdown@29.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - '@mdit-vue/plugin-component': 2.1.4 - '@mdit-vue/plugin-frontmatter': 2.1.4 - '@mdit-vue/types': 2.1.4 - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - markdown-it-async: 2.2.0 - unplugin: 2.3.5 - unplugin-utils: 0.2.4 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - - unplugin-vue-router@0.14.0(@vue/compiler-sfc@3.5.17)(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@vue-macros/common': 3.0.0-beta.15(vue@3.5.17(typescript@5.8.3)) - '@vue/compiler-sfc': 3.5.17 - ast-walker-scope: 0.8.1 - chokidar: 4.0.3 - fast-glob: 3.3.3 - json5: 2.2.3 - local-pkg: 1.1.1 - magic-string: 0.30.17 - mlly: 1.7.4 - pathe: 2.0.3 - picomatch: 4.0.3 - scule: 1.3.0 - unplugin: 2.3.5 - unplugin-utils: 0.2.4 - yaml: 2.8.0 - optionalDependencies: - vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - vue - - unplugin@1.16.1: - dependencies: - acorn: 8.15.0 - webpack-virtual-modules: 0.6.2 - - unplugin@2.3.5: - dependencies: - acorn: 8.15.0 - picomatch: 4.0.3 - webpack-virtual-modules: 0.6.2 - - update-browserslist-db@1.1.3(browserslist@4.25.1): - dependencies: - browserslist: 4.25.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - uuid@11.1.0: {} - - valaxy-addon-algolia@0.2.0(@algolia/client-search@5.34.0)(search-insights@2.17.3)(valaxy@0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0)): - dependencies: - '@docsearch/css': 3.9.0 - '@docsearch/js': 3.9.0(@algolia/client-search@5.34.0)(search-insights@2.17.3) - valaxy: 0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0) - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/react' - - react - - react-dom - - search-insights - - valaxy-addon-components@0.1.0: {} - - valaxy-addon-git-log@0.3.2(valaxy@0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0)): - dependencies: - '@octokit/rest': 21.1.1 - fs-extra: 11.3.0 - gravatar: 1.8.2 - md5: 2.3.0 - simple-git: 3.28.0 - valaxy: 0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0) - transitivePeerDependencies: - - supports-color - - valaxy-theme-press@0.25.9(patch_hash=a6c7859cddc4d023eca0c3ca0820314f6456487482af67a4b4ba28719d385f3d)(@algolia/client-search@5.34.0)(search-insights@2.17.3): - dependencies: - '@docsearch/css': 3.9.0 - '@docsearch/js': 3.9.0(@algolia/client-search@5.34.0)(search-insights@2.17.3) - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/react' - - react - - react-dom - - search-insights - - valaxy@0.25.9(patch_hash=4e8a0c516f0006cdaf6f1333cca04b01a402f6dc55935ae6f485f932da77c611)(@babel/parser@7.28.0)(@types/markdown-it@14.1.2)(@types/node@24.0.15)(@vue/compiler-dom@3.5.17)(@vue/compiler-sfc@3.5.17)(axios@1.10.0)(eslint@9.29.0(jiti@2.4.2))(focus-trap@7.6.5)(postcss@8.5.6)(prettier@3.6.2)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(yaml@2.8.0): - dependencies: - '@antfu/install-pkg': 1.1.0 - '@antfu/utils': 9.2.0 - '@clack/prompts': 0.11.0 - '@iconify-json/ri': 1.2.5 - '@intlify/unplugin-vue-i18n': 6.0.8(@vue/compiler-dom@3.5.17)(eslint@9.29.0(jiti@2.4.2))(rollup@4.45.1)(typescript@5.8.3)(vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) - '@shikijs/transformers': 3.8.1 - '@types/katex': 0.16.7 - '@unhead/addons': 2.0.12(rollup@4.45.1) - '@unhead/schema-org': 2.0.12(@unhead/vue@2.0.12(vue@3.5.17(typescript@5.8.3))) - '@unhead/vue': 2.0.12(vue@3.5.17(typescript@5.8.3)) - '@valaxyjs/devtools': 0.25.9(debug@4.4.1)(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - '@valaxyjs/utils': 0.25.9 - '@vitejs/plugin-vue': 6.0.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - '@vue/devtools-api': 7.7.2 - '@vueuse/core': 13.5.0(vue@3.5.17(typescript@5.8.3)) - '@vueuse/integrations': 13.5.0(axios@1.10.0)(focus-trap@7.6.5)(fuse.js@7.1.0)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.17(typescript@5.8.3)) - beasties: 0.3.5 - consola: 3.4.2 - cross-spawn: 7.0.6 - css-i18n: 0.0.5 - dayjs: 1.11.13 - debug: 4.4.1(supports-color@5.5.0) - defu: 6.1.4 - ejs: 3.1.10 - escape-html: 1.0.3 - fast-glob: 3.3.3 - feed: 5.1.0 - floating-vue: 5.2.2(vue@3.5.17(typescript@5.8.3)) - fs-extra: 11.3.0 - fuse.js: 7.1.0 - gray-matter: 4.0.3 - hookable: 5.5.3 - html-to-text: 9.0.5 - jiti: 2.4.2 - js-base64: 3.7.7 - js-yaml: 4.1.0 - katex: 0.16.22 - lru-cache: 11.1.0 - markdown-it: 14.1.0 - markdown-it-anchor: 9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0) - markdown-it-async: 2.2.0 - markdown-it-attrs: 4.3.1(markdown-it@14.1.0) - markdown-it-container: 4.0.0 - markdown-it-emoji: 3.0.0 - markdown-it-footnote: 4.0.0 - markdown-it-image-figures: 2.1.1(markdown-it@14.1.0) - markdown-it-table-of-contents: 0.9.0 - markdown-it-task-lists: 2.1.1 - medium-zoom: 1.1.0 - mermaid: 11.9.0 - mlly: 1.7.4 - nprogress: 0.2.0 - open: 10.1.0 - ora: 8.2.0 - pascalcase: 2.0.0 - pathe: 2.0.3 - pinia: 3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)) - qrcode: 1.5.4 - resolve-global: 2.0.0 - sass: 1.89.2 - shiki: 3.8.1 - star-markdown-css: 0.5.3 - table: 6.9.0 - unhead: 2.0.12 - unocss: 66.3.3(postcss@8.5.6)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - unplugin-vue-components: 28.0.0(@babel/parser@7.28.0)(rollup@4.45.1)(vue@3.5.17(typescript@5.8.3)) - unplugin-vue-markdown: 29.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - unplugin-vue-router: 0.14.0(@vue/compiler-sfc@3.5.17)(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) - vanilla-lazyload: 19.1.3 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vite-dev-rpc: 1.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-vue-devtools: 7.7.7(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - vite-plugin-vue-layouts: 0.11.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) - vite-ssg: 28.0.0(beasties@0.3.5)(prettier@3.6.2)(unhead@2.0.12)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) - vite-ssg-sitemap: 0.9.0 - vitepress-plugin-group-icons: 1.6.1(markdown-it@14.1.0)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - vue: 3.5.17(typescript@5.8.3) - vue-i18n: 11.1.10(vue@3.5.17(typescript@5.8.3)) - vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3)) - yargs: 18.0.0 - transitivePeerDependencies: - - '@babel/parser' - - '@nuxt/kit' - - '@types/markdown-it' - - '@types/node' - - '@unhead/react' - - '@unhead/solid-js' - - '@unhead/svelte' - - '@unocss/webpack' - - '@vue/compiler-dom' - - '@vue/compiler-sfc' - - async-validator - - axios - - bufferutil - - canvas - - change-case - - drauu - - eslint - - focus-trap - - idb-keyval - - jwt-decode - - less - - lightningcss - - petite-vue-i18n - - postcss - - prettier - - rollup - - sass-embedded - - sortablejs - - stylus - - sugarss - - supports-color - - terser - - tsx - - typescript - - universal-cookie - - utf-8-validate - - yaml - - vanilla-lazyload@19.1.3: {} - - vary@1.1.2: {} - - vfile-message@4.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.2 - - vite-dev-rpc@1.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - birpc: 2.5.0 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vite-hot-client: 2.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - - vite-hot-client@2.1.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - - vite-plugin-inspect@0.8.9(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - debug: 4.4.1(supports-color@5.5.0) - error-stack-parser-es: 0.1.5 - fs-extra: 11.3.0 - open: 10.1.0 - perfect-debounce: 1.0.0 - picocolors: 1.1.1 - sirv: 3.0.1 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - rollup - - supports-color - - vite-plugin-vue-devtools@7.7.7(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@vue/devtools-core': 7.7.7(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)) - '@vue/devtools-kit': 7.7.7 - '@vue/devtools-shared': 7.7.7 - execa: 9.6.0 - sirv: 3.0.1 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(rollup@4.45.1)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)) - transitivePeerDependencies: - - '@nuxt/kit' - - rollup - - supports-color - - vue - - vite-plugin-vue-inspector@5.3.2(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - '@babel/core': 7.28.0 - '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.0) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0) - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0) - '@vue/compiler-dom': 3.5.17 - kolorist: 1.8.0 - magic-string: 0.30.17 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - supports-color - - vite-plugin-vue-layouts@0.11.0(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)): - dependencies: - debug: 4.4.1(supports-color@5.5.0) - fast-glob: 3.3.3 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vue: 3.5.17(typescript@5.8.3) - vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - supports-color - - vite-ssg-sitemap@0.9.0: {} - - vite-ssg@28.0.0(beasties@0.3.5)(prettier@3.6.2)(unhead@2.0.12)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@unhead/dom': 2.0.12(unhead@2.0.12) - '@unhead/vue': 2.0.12(vue@3.5.17(typescript@5.8.3)) - ansis: 4.1.0 - cac: 6.7.14 - html-minifier-terser: 7.2.0 - html5parser: 2.0.2 - jsdom: 26.1.0 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - vue: 3.5.17(typescript@5.8.3) - optionalDependencies: - beasties: 0.3.5 - prettier: 3.6.2 - vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3)) - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - unhead - - utf-8-validate - - vite@5.4.19(@types/node@24.0.15)(sass@1.89.2)(terser@5.43.1): - dependencies: - esbuild: 0.21.5 - postcss: 8.5.6 - rollup: 4.45.1 - optionalDependencies: - '@types/node': 24.0.15 - fsevents: 2.3.3 - sass: 1.89.2 - terser: 5.43.1 - - vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0): - dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.45.1 - tinyglobby: 0.2.14 - optionalDependencies: - '@types/node': 24.0.15 - fsevents: 2.3.3 - jiti: 2.4.2 - sass: 1.89.2 - terser: 5.43.1 - yaml: 2.8.0 - - vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0): - dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.45.1 - tinyglobby: 0.2.14 - optionalDependencies: - '@types/node': 24.0.15 - fsevents: 2.3.3 - jiti: 2.4.2 - sass: 1.89.2 - terser: 5.43.1 - yaml: 2.8.0 - - vitepress-plugin-group-icons@1.6.1(markdown-it@14.1.0)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - '@iconify-json/logos': 1.2.4 - '@iconify-json/vscode-icons': 1.2.23 - '@iconify/utils': 2.3.0 - markdown-it: 14.1.0 - vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - supports-color - - vitepress-plugin-group-icons@1.6.1(markdown-it@14.1.0)(vite@7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - '@iconify-json/logos': 1.2.4 - '@iconify-json/vscode-icons': 1.2.23 - '@iconify/utils': 2.3.0 - markdown-it: 14.1.0 - vite: 7.0.5(@types/node@24.0.15)(jiti@2.4.2)(sass@1.89.2)(terser@5.43.1)(yaml@2.8.0) - transitivePeerDependencies: - - supports-color - - vitepress@1.1.4(@algolia/client-search@5.34.0)(@types/node@24.0.15)(axios@1.10.0)(fuse.js@7.1.0)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.89.2)(search-insights@2.17.3)(terser@5.43.1)(typescript@5.8.3): - dependencies: - '@docsearch/css': 3.9.0 - '@docsearch/js': 3.9.0(@algolia/client-search@5.34.0)(search-insights@2.17.3) - '@shikijs/core': 1.29.2 - '@shikijs/transformers': 1.29.2 - '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@5.4.19(@types/node@24.0.15)(sass@1.89.2)(terser@5.43.1))(vue@3.5.17(typescript@5.8.3)) - '@vue/devtools-api': 7.7.7 - '@vueuse/core': 10.11.1(vue@3.5.17(typescript@5.8.3)) - '@vueuse/integrations': 10.11.1(axios@1.10.0)(focus-trap@7.6.5)(fuse.js@7.1.0)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.17(typescript@5.8.3)) - focus-trap: 7.6.5 - mark.js: 8.11.1 - minisearch: 6.3.0 - shiki: 1.29.2 - vite: 5.4.19(@types/node@24.0.15)(sass@1.89.2)(terser@5.43.1) - vue: 3.5.17(typescript@5.8.3) - optionalDependencies: - postcss: 8.5.6 - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/node' - - '@types/react' - - '@vue/composition-api' - - async-validator - - axios - - change-case - - drauu - - fuse.js - - idb-keyval - - jwt-decode - - less - - lightningcss - - nprogress - - qrcode - - react - - react-dom - - sass - - sass-embedded - - search-insights - - sortablejs - - stylus - - sugarss - - terser - - typescript - - universal-cookie - - vscode-jsonrpc@8.2.0: {} - - vscode-languageserver-protocol@3.17.5: - dependencies: - vscode-jsonrpc: 8.2.0 - vscode-languageserver-types: 3.17.5 - - vscode-languageserver-textdocument@1.0.12: {} - - vscode-languageserver-types@3.17.5: {} - - vscode-languageserver@9.0.1: - dependencies: - vscode-languageserver-protocol: 3.17.5 - - vscode-uri@3.0.8: {} - - vscode-uri@3.1.0: {} - - vue-demi@0.14.10(vue@3.5.17(typescript@5.8.3)): - dependencies: - vue: 3.5.17(typescript@5.8.3) - - vue-flow-layout@0.1.1(vue@3.5.17(typescript@5.8.3)): - dependencies: - vue: 3.5.17(typescript@5.8.3) - - vue-i18n@11.1.10(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@intlify/core-base': 11.1.10 - '@intlify/shared': 11.1.10 - '@vue/devtools-api': 6.6.4 - vue: 3.5.17(typescript@5.8.3) - - vue-resize@2.0.0-alpha.1(vue@3.5.17(typescript@5.8.3)): - dependencies: - vue: 3.5.17(typescript@5.8.3) - - vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.5.17(typescript@5.8.3) - - vue-tsc@2.2.12(typescript@5.8.3): - dependencies: - '@volar/typescript': 2.4.15 - '@vue/language-core': 2.2.12(typescript@5.8.3) - typescript: 5.8.3 - - vue@3.5.17(typescript@5.8.3): - dependencies: - '@vue/compiler-dom': 3.5.17 - '@vue/compiler-sfc': 3.5.17 - '@vue/runtime-dom': 3.5.17 - '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3)) - '@vue/shared': 3.5.17 - optionalDependencies: - typescript: 5.8.3 - - w3c-xmlserializer@5.0.0: - dependencies: - xml-name-validator: 5.0.0 - - webidl-conversions@7.0.0: {} - - webpack-virtual-modules@0.6.2: {} - - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@4.0.0: {} - - whatwg-url@14.2.0: - dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 - - which-module@2.0.1: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - word-wrap@1.2.5: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@9.0.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 7.2.0 - strip-ansi: 7.1.0 - - ws@8.18.3: {} - - xml-js@1.6.11: - dependencies: - sax: 1.4.1 - - xml-name-validator@5.0.0: {} - - xmlchars@2.2.0: {} - - y18n@4.0.3: {} - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yaml-eslint-parser@1.3.0: - dependencies: - eslint-visitor-keys: 3.4.3 - yaml: 2.8.0 - - yaml@2.8.0: {} - - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - - yargs-parser@22.0.0: {} - - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - - yargs@18.0.0: - dependencies: - cliui: 9.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - string-width: 7.2.0 - y18n: 5.0.8 - yargs-parser: 22.0.0 - - yocto-queue@0.1.0: {} - - yoctocolors@2.1.1: {} - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index be6a22dfa..000000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -onlyBuiltDependencies: - - '@parcel/watcher' - - esbuild - - vue-demi - -patchedDependencies: - valaxy@0.25.9: patches/valaxy@0.25.9.patch - valaxy-theme-press@0.25.9: patches/valaxy-theme-press@0.25.9.patch diff --git a/public/algolia-verify.html b/public/algolia-verify.html deleted file mode 100644 index 2b0a4d3cf..000000000 --- a/public/algolia-verify.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - Algolia Verification - - - diff --git a/public/img/dev-tip/correctcontent-dark.png b/public/img/dev-tip/correctcontent-dark.png deleted file mode 100644 index 658c7cdd9e019391ecc8cc8ce923312b0a6f0895..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3470 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 XNCf3IPTph0fu@l@Qm^5f^UZ$(_2n4s diff --git a/public/img/dev-tip/correctcontent-light.png b/public/img/dev-tip/correctcontent-light.png deleted file mode 100644 index a0b1cc08a63d622242f23bf3de0402b1023693b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3600 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 diff --git a/public/img/dev-tip/correctendline-dark.png b/public/img/dev-tip/correctendline-dark.png deleted file mode 100644 index f5f83d037ef5a86605ee1deda00bc72990471ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32076 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

{NYq zYYXe?HR;qDX71US;GF_c7oggKK

-Z^Zd=P|?Gp<$4y$gHCIH?bjLVgfz1hQPU|w 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 LtH~A0JVyQ>pCXfb diff --git a/public/img/dev-tip/correctendline-light.png b/public/img/dev-tip/correctendline-light.png deleted file mode 100644 index 76ba0fdae81fcc0b20afbb7f6698bac8a990c63e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32661 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 diff --git a/public/img/dev-tip/correcttitle-light.png b/public/img/dev-tip/correcttitle-light.png deleted file mode 100644 index f92d5723d194f005acca9d86f0f425d1bafb8eae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4865 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 P|9Bhend;W-IKTK8$GM7Z diff --git a/public/img/dev-tip/correcttranslateendline-dark.png b/public/img/dev-tip/correcttranslateendline-dark.png deleted file mode 100644 index 0cb9fedcef17f8042e7dffee166439459a704d48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167244 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

3=^ua;MDpqY>IyWthM@hQ|0=OE-KzAo6 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%0ABYqcmtH@8yD* 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

At7k+ju>zWYl7yJXg*B}1l?K3C4Y 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%0DZnj6aC0Az-wy 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 D^q#~% diff --git a/public/img/dev-tip/wrongcontent-dark.png b/public/img/dev-tip/wrongcontent-dark.png deleted file mode 100644 index 65b9b3c481a411e320dff5923dd3a32ff68316b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3613 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 diff --git a/public/img/dev-tip/wrongcontent-light.png b/public/img/dev-tip/wrongcontent-light.png deleted file mode 100644 index 9ec0a29dc071fab53e6e8d202f11bd651f30f739..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3744 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 T%VvEFV$b!pjkPLoJ3jswrH|=G diff --git a/public/img/dev-tip/wrongendline-dark.png b/public/img/dev-tip/wrongendline-dark.png deleted file mode 100644 index b7bfa5d5b1a063674791f4e1ea389cf1782cb541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31513 zcmdqIbyQp3*Deah-Jv*z;%)_s28vsNKyi0>mlE75?gS6+Zf$XQFQrhR#T|;D@V>wA zeCOP8$2ez<``^vTPIgxIlDXDgGN1X(D4>cg7CIR^92^{$yxa$MI5;FA92|TNDgrD8 z;I$6>goAfemz9L8nWWf*{Xnz=CJu;?OpxH}cV&xRD zck$0JZTJ+P#Lg|`;1;NJ9b>$Hk{W-o2N~$gkAYH7}`ZFRkv7RnpWm zwk@majEKurQq%h&ukP*>DzEa9M?gF!s}xe&7!VTgVn3u;QL+U#8XB&1ae%9<=}-F-jB@CgCZ^D0y{^y8BA!(%g2vdf%3 zgL8}PBt9q?RkR3-Nb49{JGuqxo7#K&g^Nil7FV{$rR3|G*m(y;7C@V& zK0(p;t^pzv^3w7eF`siav`v7TM*1dp-27t37EaEdpKP6dJ^dp-rx%6Aq(>!Wn_9Wr zIQk@{6`EPQ7FV`?)VB-@OZ=Qs43136gj9t@r>bcgMZ{%z`9~yYm8RvEN5*G)_=Sr~ zD&!W`Svz_sWfU8jIha|yWf#_%+qlUoYlX$8TiSbm3jgdC5UB{%6_-*H5dIJnl@gVZ zW$oZ?YUQG#ZE9rhXk_8!4(rLH+T_d?q6Q7VCB5}=Sg)TLD=1Ya&dEUz5Khka4k5xYVQjN z=M^vi0RZwgJnFs=A{@#**^rm#`Q|I5lwkgb&El;N9kTLUjzW)3TA^2Lu}%2+l3M0& zXfgN%*vrvaUoGXq)x(B;^(!I4RV$w+-ggRhZXC+@Y$S{NJ$0T`I<9*h?2)cZ?D{;z z@mA;6_~upIxv!61;35M4|M@m={`=!bUb@7$U>~WWyyFj2$OPviuH1s7e4|q!^8UV& zElu%eiSLC9A|UaEPY>R>^1QqL)hBya&#yms7>pAwOFIIQ1E-ai!`RZbVbA;yC5E$B z>uGI)muOi=%Kf6^BoLYGT=+eP_&7i#-G>)UGwhW~vAVc68p20-*E@Y$JqZOcGo?PcpsMy`!mrSTS6l^$krhw zL?7sbBWahtnEHtZ6--DkIxJf(2$Fb={o^bRkj6!1MtNr%29BmdZE)3HGybD#bWTSI zf)%inL`5hCe8U7q1&g#+Ij20sHR0Hi0D*XjK8`w@BJ>)t!j31LsGrEWRJZCK~>`dzHGyRju~i!7^{h_(n|l z?P^wyKy4Matz+RN@P`PFgvFM(Zl;PVHYq2Y z_~m+)D7EmcmnM6EGmE$N*-e#oSpm&Te!u=y-Fax928pza9kV8X(CggrkD=mgL589k zSfQs%w;Zb5lpLVDpxfGIf2Jd#?9gA-2aer1M?oO(wU3qpMCuuu?|Oo)u@D#d9F$c8 zZFD$qQe_*eJ-(La|<#cF;^IZ4P>ffg_bySG=hL(*Vv0RRFj2>F>ik-@eN3>L4D z77DE;#fw6JR}XwXoZe~J1$S6NAYz))S)t&Lj<;*k;0iFf0}}|$XOUKx1Q-}3IYGxhz_-XI^T^Xkd8G>5mL$`0`sc>QCPvOtFGNN5q~H`6Ib&c!ZC8b4UDQq>arN%ScJ)irmj&JNioK;kr{+v3LEQ4_4G2ywU>C)t^ zL~wmWj%rC6)x_ zuWFvyt(zvb(mth`x@UEb-6LT!Cy^)N)jHO80bXyA%EP>NI5FqOt+^7kl%}E2U_+Oc ztU#x11Wv=eUCva)_=S+CA&?Z8nx^lzXSx<2A zTsM!7Ptm2>Q`4${kWZAA*MIau=4-6V5AL{2CquRImRHe*KG8q=vajQ3GhKB`aY*K+>@CaN-8Y3u@~5@e4(`*>@F%X| zOyL8Xi%QSySOdxjh2MlhrEHdI5C#-=oiQq0{UioWBgThJWzGnJJ#dzNX zIntH3%0nh!BiqI+(!22jqd7a_49`6(!#s$V+DElgh83^5MK;WbU4~BDxi5SZvEjcR z@`$MDn@|Pqu?zw)=X?i^ zzI#VY9dQKlpEGZDo&wAD&0CW#;s;z=A;_Tlz_nNuZ?1&)u?BUJEF7)Ps)=9mak+8O=m);0Y6MUgID3ppEqVs=kfD znD(ty&tVJUha;j;3wCh?4XesDxYEhIT=3&+-&=QoH#Gtf{&qa!`)suDmL#2ClBXLH zkCFxsIS>Zq*=5Y_#f%MMg-EVFw^v2oVpzomG$*r&{3}{9s!ozdK@l6bU!i|HO?2-t zHiA4*i36f9(0DsLQ8E^M{jy7WdK%Q*y#*t?x&IKiS}PgID zCUs)b+T*t~;qS&{i)`CjwohNFvk{g@IkZbNXHGkwQNHBOAHDD$I2iG|&r7b4Fi<Te0YYt_df!oX`e)WyMRL_s|sq z$9_8*xt)p^jYnC_9={-@@`6jW|WmJMN6@ zXUIo1%#?9>M-Qb+b`E(itRF2ZW?HIs)c6B_Q(+z!ktod_TKlW~q;ZnXqr}_%)A9o~ z^inJX-Js^CpaU^AQK}00`#R}*nX?j~!PIUWu@0K@OehP6zF(!)8&2EiDn!|m*`|d~ zyf2t1bj=+|DTQ z7b1V_5%Z}=A}Pm>L}*fBD2RzR{tvkSIuDkJobDp+KJei5nF365&+X9#0U1gQo3 z{*SKHx-TvKV?R`=0szXOGmH)!Ek2f^9bQ^KJ#yA>a$?v7%sRl$yu)4g(Nv+=#SE~1 z;YLa=d#Ip-4G6A_#wF;@l)(xQ8gB#72gLi6nE4Q6*{{! z@Br{D#uM3Qw4R`F>kPm215}XUJphO$oUmhkcOzj$@<Kt1 z#gM_4NVZ09YWWH%_xLXE!O@Nft7pvi7p-bS1=>>*CT!2Fm#7(!!aRwKWLOS-L%7e# zt^*T!kHkqi?i?xg9g_t?6q>CaIlJ|DrMbRX9wd*fo;xCYy~fXlXE>!WSqBMylB`>Z zv#f=|YA8b7{U9);Bm0iUT{+x3cnu~iVrqA}c4mr#hC@J8C z-&`S7|K*-P%(ku!QFVG%UM*ar^~6@M$U@+*r1^IWw(7o&rftmT+cho4RZN(ww;}8C z>&c#va+;$vV%TN~a(A^R;}D!PRHja!H?lx!6Px~|lv;yr8GdokC7 zViJo#y2xaZRdT@O;g)jHPjMv`a2R!kcK+9hrR#xh!+bgDvw)MaH&v#);Y`N`&jtv| zNkkYGRg6U8%NHtR%mfw~w&sz4i?^xi0sk~RhQK~H50g^M^MOefh=K6=Ss!fclcClE z;Sg2tU29pLEzP!+EmF~XAmr0q06g*_YCd)<-xW|$?t*RUn;s5vL08>{t#NVY+oHY< z1Bf233dmIH-~GSq)megV(mf2~Tu9RfH3oPhvw7ZPnWcJXw2s0xXra1uiLHp2B8gNf%BX;be@0wdBR-O=Vxt%3 zRQS5y9>D&?8BLgQ0XEKdY^k!Yxc=mU?25o5lNC4SGKemfWeOv7Ap2olOK*zO7`4Dw{8#~x%7UUhrXmBZQh`Rs0?!csRB`s79HggP$0L;P# z$^Z)u9_zck0`z~>>~8&aih9{nBuVIJl<{DgX-`>22|-@;FIB(uPm7xB&1UW2hjWfHj_ap98d7eq+M{sQ27SK%pLvfRY% zXjcVMn5(jGD^<2>d8akJTKo;IFy8L<-ahINNVD=TuF%ntg7s)mu(KE<{{*)~MWt$0 zu=?kgjxy!)JzMmOo4QaNjcfn93;tcGl`5Z-Nq94(!)P9yW3Rk~rT>0hoKbL5IouF{3&QN|R@O_6dbF}0+=lMiqS-PN-0$#g z+*S7CFV&1^viNZzaaL?pk86k&RKKW|48i?T=%T3qC)GbShKU8R8Hlui?op(2Xmpp4 zuj|#A#-vNjJ3C3jzt~Nc0`-WkVaT{!ya~wKclU^GxopCg@k*Fph;Su#-4RSY$fYzY zDODhvpCz8wjF-B%z>C4RZ?=BZ`dmGC(v=at$hB9hb_c&ZxTS^Td14;y*ei`{a>b|! zgdG9$5S@S5bX=ySHPWhoNY8r`4j4Sb7}rayPJ`%{>dZhCRmoe39zH*obA{No%x?7& zJ0DYFN$>h&Z3k*6_VLJcq|rbHUS&nQ@7R1Hul1DFXuLX?JN5oyGr>&_8929q4{E=>R1qs3nR**rGf%m~N4>*>r%H?wm_GcOr%^kz z%v*AeehAZpJuR;TU-#zphsgIs=GgVosTd$u`WkFeCF2mKH2zpvSErkZRHiYhHO(5+5-} zz_g;3Y+Z)kOjMd7Q{a=e6`1mUf_8aAudxL}MrAq8ri9k$v_r$JKAB>L>Xg~C3TElU ze3`yF1uBr8bUwXGff4dT;x^}aRAtpAe8gt8V_lxp_CEnF_*t|MeUc-Ra~l`bVRnCt zG8!7}*2o6*<@xCi*a4~8iHJC6exh_<(hi%8&&C@>HsqUYh$F`TsK^maMOfa+28UVu zBnR&Rtwv^?>6>;p2Q8n*4;`7I=Yhwr5fI&i=n1t|aW{`3@uFq0t>eWmE@=xl1Ww+TH<%e?zaKbWG-+ShC`(pT+S(u{9|75?+I0{5!Z5^S zakH)DFO)n*LlVd;0BvkJ;YD09$s)pP4=`%e%&}lyL*#tD4kG&T`G>lqktxqilRG;A z*nOEMjso}fiPu26FnL13I`GW>EewKmnrS&P%UStQ5_7}1+A?}{zyD3EGOeZs;%hI^ zEBtZl!`O;aTc^5{1$F4k>cJC+a)9QlwLFj~Cf6sLzNrXyc8rGQp&ml3IC|-?dIH$N z@#EtuAIxyxvpHLLZkGZc0SB%F3XRxkvFD5>jsO9vwyik?abz^L-V3 z#NW zIBL11ZDQSj8INSsJ3Wf@9=@weDNZrh{qEA|dUoe!Tx(Qc-I4eFeFXzBTKsgHTc#$n zKaQCzXP3=7RJpS;)z$=I0~`fjRJ*p4dc)8dANkD{ym@?OKSQL2+-}@rr5u#BTG1(M ztmcORJ$w~)r>lVHMAof02lP<-EMivZLUPZu#Z$R?nr7)gP|F}0O9Qb8J-~q7bS!9i zjtHdSGQ#$>v{3Y6F8aNDh!t^Ivv@nAkJ!pji(>0rD+t6@fv*}WCE#tTCIOIM497B* ziB1bbMrSGeuM9|pAoHuwo$(sxHkHpSlZ1&`IDhfF*tr6H@-Vx#eE3k~XXiro)*$&* zc;pX4t2#GvD`=l?H^Lec!!ncCo!;RNPdqvKD2L=TP+3^}CLVTTF#B5CdmoKoV!$V6 zMP#qEF1~ZKm(7XSV;&QywgEOvY`{QUqwXx;hr{o7!_W@Ky)QK84+fiJ(-y0_2E1}`jM>M zNNFqEhYz~=xv}^z3CnId(?1-6{NQ*r$SQ7mO*& z@Be4%5?|E6z8Oo*&{!amZ7Ods;u8BVv~R0k$C`Ei-0t`;mK}=F+dfhg_WQHJ&IY`t z%51Gxe63??Svtb)k$Mh9>zoNOeG5qwhhN7-OA08JpKMvfqcg0(MMc3>htVQ+Kr{FC zh^T-f?YEQC&>@LYK<);%fOw(Nf5au6=SnP1zYSY_Hyqe+Fe=6E3X>L%W4iar06`f_ zeuK#S|D&iw$h(rR4lS2q$JOA!YyyPjC-*6n9t1LQf zuY^1jJuS2Gu_fxQJ;qPDIs&>)V;d~rEP*a?o|Y-TXa(-bqpROXzz~L5rgfv&Av<%3 z$Lm)oKj=QB1ig3fgreG-@t%6)|>AfVcD~R8H&{z z5#ZZFerO3!45Q&+JxExmJ$<8yuvXx~<&yio^Ms@&j;AbuKw@_Q92A5Mw%ZKRgr2pY z+)+LTJXn0T&pHAM!{D(%72~F*6MKf~FGfh!4nP-9kNP=k!;C`W?DmoVsW0;@74)CV z8?DNW#k*6mdV0%3$k5vx5A!;G$l@k%!|tw^fG@UqtE1e83aUrd!K!X8Uib08n`1nifK(^i`1k?RxXHm}HO( z`zLg<0a1*%9-%wmy}SW#5Q{%m(6>EqILOyC7-hDQ0O5O$n-(AxWUy}wrYG~=iPdG@ z;Tc-V{(W}1j8qS{4b57fu|}^5WG7Yz29&-Uz3tQaRE~1_?bY^82Wn z9B5pzsUpOX2Sa;AGm>hdh;wWa?+Z=L(?*bwd)?C_C3M$5>Hkd;PY~-HS7r!A#`ZSE zkx2MW`Sa_OraQGr23W@yaHB#MclkywJOG8Fj-1EPqm3hT?iY+XVXSMw4D_ss4L>M0Vh)G!{Yso2XxQXNrzGH_Hbj= z*tO8cRDlVi^){$PErm2RY*Kzg^RzsE_X?-T56L$E1YS+@B!&f1ifDdFKTqVu1P`zv zr3}hlz@1NI1mvMe7lhMq|G@#r_u^$=! zS*Pc7RFjj$pZ7%bqMI8>-_0fOrONU6=oZ58b`HyGU8gFuaA*GPV<4sWKA|gjsgSGl z!*{PDVeGl`K6B55?qni8UPO~hg2~3(#KV(><0ea7$`A&bt>MnR*IVd*D}<0iWro-@s<;pezc^gmOO*KCO!k_xEY4yQjdej(Ho0Q46l*OV~~&-=2%k zvJ{@j!xC*QMv-bP-fn5JAyR`iwhi%W9Z4v(zbC>;bdxfoX$Icsj3Awtf8w2SpzI-WtbPvD1vucr~;RMVQm zc+pIAHl5oupI=KiF7Zgh)D;u3L-!T)J;j4N;?>Nomllwm^%LlU_sFEBP%RvC?t@gH zjIsY|>bvpj9{H{mraf!|sr5j}U_}$p*DwqpJ*j40e`6m3cMuuxa#WM7k&V%0Okb}Ek~Yuq$`M0^Pu!`6BP~P zGLi*QA-SbKC*|IaxwUEKpV6B#yoL}`?`G$)aKkFMU?ag6s*B>-f1qfE460`1TQm>? z)&<~D`Z&{?gvyk!LJ<}(k(Zjvheox}wcJ)gg1ZmPhh3oJT|}2jDg4l12L=HqP)4g9 zUNGn`70~gXIe)#`gm0@%87+cLw=E8uoJ_%|C9zStJMeE?Lo}}o#|u@mw|J~kD^jzS z>;Vp+!SBCMYFM?c=e;XI+vjF>*sC@3<#kBEU?~;bQy4Q`Y$~rK*w?R+gK{PyCnWD; z&~CUqVCDlRZgUb3L=@+Z9yMbjXXyqCSC}a}I<+X` zE`wA2L?f<8aF3zfmHKYh@B=XQNsXr@T1o zF$9^6y*$1ufv7lzInAH^F@qYV&y^DTQ_Syyr-j}8tq={1|mmE&L?DtK(52(Q;tQ18ex z-q+i>v5A|}^;H;RFr?<77@3I3O}B(9al4AMwM78i6gK{~B4rD%5ys;NVzFJ!`)!(F zFrv+I@fZzS!5FVO2#?t0jaLGK|y;DfOyzr!s_Kdt9@xag;?q z+(>I@ay60|`LcA^$4T@B-FltuFm6d%%yIrmo07`Yu#Qt+p(v4@tTE$sONSh9cg7R0 zQ4AQ2^IK;oFp%rcdP=I~M{TPQrlc~~`ysJ?hyi~H08GZ=Wfx7ai+M8#g+2Q(8Cpj2 z&Yz-jBlEqxu(I@$El^)TE!rvNAd;?hi{79*bo@onje+o-j?!@{>e+e()Bw_*2>u?^ zxUol1=B{cc5}0s>Qex>&1jQI^VS6~l7ot`CGoV+2%Y(~>Rlkjb`oAT8W?FBgRC zi<7axS*+7nv3bPttoFX9?WH2|YWlI8&^D+LDh#SBpiQ}9u|7+YeizUJc2vC9x`tm+ zOoz0%_vSVQ7%3GjKvbhhKQcX0c@jUe~+qIBwr*sj&f|qEabQ1NVCe@6c6xm%xq-h22)_rSkA{PztJq;iVh27I?=LZFi#PzxAR>lY zKL+y8n`NZlx89lVO5x!u;bqgmCx%ffdI+cPB zPiK|VDsz3T(xN^U#F5Mn!wnPXW=a;7=lJt{4-#{iyXcIxk!A4DZ!0-Cw05jwSBvl5o0X+LSpC11Rh0>;Av-X{|4eI70pORPoIg;< zXFqG;s6rY*V18PP8|-*%#>NDd6M{f$a230%Fzf(l64B2dMfDsbuVG`Oj?DyR?Z@Xl z_@)aKN!$urx2{s;8H73AbC>;+@Vzp!TW<%*UX|;*$We=aO~{S&(xu`Vy8kNG8NX9% z_>{O^R;ij3KF3%%1XY5WhVa`gli_skmsKs-%2uWdh5x&O_c)B28dzHyR34lwJXGua z>)ZI;tGV>wBhz-NvLQd2#QP1c8sIIZ{X6Um()7y6ols0=`YONkN+pz6ijF(UVJ=Af zj^!ilZ{%f!h#vvFj|GfS;TVVyOtIs}B=rtpwyhj{7b~ZZ)puNRxg5l>qd%l37D(DYBymk(_kX#p%Z)vlfC1_|3{PIO8E*UkGgo_|0*-||YlDjUfR!&Z_9rkOdxe&edO*I(JDR3fBzY39D^$w2cy zO1r^J=alC%K_8IejaZMyPyF|I04D>p97H?X@$siO_H9+b{&Qg*UYTG-^!8PaMD^W61W#E^0&|?TYupNOhpA zgrn(NQ6*=#r8>Xmo7N55y6h5WtZli1#!SRx!gF3Oj}}`r2#H*KCusm^g2gMOmA?b^8enk2x9~|=Lh+sK0uA|F_b34FSr5OJ2^7@!_MJQ!r-%wX zhMn}jM~vaK`%4S?Z!=y6psXOXR`5ek*l)u9Bt`OB87`9qz0ocmpqba>-QTA??A|&D(0oMDw#E%sx5CL^y!*I4;K>WgA<&99IQ-S4 zAxim74+%yt1|rLMH@Ar`6?y!5khDSYDv}1yci1>xfv^&HReHS$hutqJ5Tt-uzaBBS z<=m_`<*?7aRwzrW`SLZtD;gYLMv#p zO7Y@i%=LNl)CiR!~cQ@tlVH@4HC}xn>SG^h?4sM@|im{s(H;@WmW7; zJm^Cbg~!4^p1!hlH@1nvvhV)$Pv%{1A^W{C%i+9>dr$dC26FEY<;;gjAjXQ)1!~Fs z;cjG0m$J90``j~P!r5jYVaWK(9dNrh-e5Ke`%*&bmyPCUO*5HQpHaP*S$9SfZ>)C3 zjt_p;FI0(m|C^gl>C_LYr(KUC>3)V+Mtyc^y?pvqEy*sTI^i&Q7p40_<0G}7O-oA@ z#g#SMuKCCuQmR;O#Cocxqs!Ub;W_=)1xEBM^AKU4rjyx=$rAP7*8Y9bd2gUf%w3WM zwcny+q9HBbKYHM^dIA}|X*nwoNgQA%(T}axSTE!)6_^`a_q_tz!nU@`_Sem8b);r* znPT&mvEibVq9_(B)F2-|j_5*C%OzAjV2LBI5qgXZ-J}qZp=u>%$T0Clp zL#RG6sQtEaFhePkEjxy`XoD4m&&n}OCrXszR@D9QBWLrd+-1WtD(=j~3z)l3D_M+T z7yxBbLgQzLr@)ziq&B5zabO=?R75Mtx*Vpxw=M?(M2!#>2V}dxnb3az{#d{Otzb%gktH zs+r*q#3*btktnG@u#S&<@am{G(ZQq>Cajk7@QwH7$RPFy{vT7*yZ9v}sFZgN_9an$ zy74TQSelpKLy1s)J}x0DQj;P5fgKZJ(xv2cBz{q-`$NMl5mCeIk1H$RO7D*_8f3F} zC-b;A`Z00R8K5ouDa_D&WbLq@qZC1vtAoM_GX%{~C1f_MJV;j(e?rpKk@&k2jGumO z(pJOs^2!8@pm`gd&1Fm29;lEK(Mka=X3R3cSsAe{Qp^Hi>*C)tNxmqptEifS(nBdi zbQ-;#G!cMYpP`NTa7 z=8(2(2#f*cb+EZA)%h>~;Vdf)iqEzZlE&z}QMSX0l^$_quCF>u0&=MGT0m^(!WB*4 zorFFNr1X2>gHE{Z)HIcyqw3?lkPD@YT)FQCc#!y6r0Fp#KI(i0bs1zLmY7)%GT|Cl z{pk?bIv!5UIJF2b6ALwqRx*WcZzaV+C`}PLc*;o-IlzLXMfWU2>G)ozt_lT{4luP( zReE8*X<_h^YXBehNK$a7FcPxg5`JaQA5QEGEj^nt@)=!g(cM#-q1JtEiX%cf*~B9U z+rborz8Erm&EY{9KTi)IdszY*$M8X7(SwlM-lZGyN}`1&EbK2Q|EFj=8Zu>$+8QR~{IT#Rgoxqo&SDu;k0!T$#1} zj~F5uuefmsly)xAUw;=91(j$>d`%Hz*YM&8INk)!npTR8azq_)K>aSJZ?;G-32;_f zgwm?Va6w~#HSs{TPlpLQidErL6I}8jv{7N%C%FN!9BL~h)1T)r=M?!{a26F|5!aij znudljX+9ZmjsbP9`sy}A@aXD8!@vm_Y}F-;vu^9IbZQ58#4?DNJ=M0NKFruxx{QxQ z`EQHRFbsD-Qy^|cb6S2?!2M@E9nDU-O&P~cNHa$<3e}Wa=y6rboa6Bj4EpMKI4QhH zR82Y_%j3|@Evi(HDA|b{?W_P0#JxL#fk zSA0!Lo?A`y-HpTzieRQTkwfq8_xRnhg?W2h;C)KmU%W38G=`0!(P9|l1kp3ZTUMn* z8r}shgo0j=d0Wxg@(sG4xBgZiBbs;ak|RyoQr;S2_JY1yXyj-v$jF?bN=b?o5+Q6f z)&Y<1TGuIq6<+pe`vw(f5yk%Exl7}=HrjsF1q!H122YvAeu(-YImx=uP>(6rd7|HK5l{@U(A)fs7G6AbL>i;t_eG z*$BPD+7JKJV(}asYtj?U4+4g^qe}yKz}h?p6oH6dfx1#<7{b|Tn;|DG701=j_>B=QuRb!*Xm$A&?bjZi&pX~&nUW^hzBhW=+0LD{)ElQw;28^?DE|;!BgRcH0kPO8 zE@z%CNC{cw!B{BR2hm>tG6x(H)us;)4FA304?ip?(OoNUSjRC%TxinFeOJ=L>fQuo z{}ltnhHg3#HQ5#LTzu1@_;?jk>%nsWY-0vKOr?XxUa}f$g<^ap>%x7_QP~My#0M2r za*qK2CfLAf$CI$WPtHgU=_lRf23LgcFLwOHT#zSXbwE!2RmVl5^@IIaZZLEvBL`pk z$qv4*YZLQo8xu|l|LMnrD!JA?hwogXDVp!_SMIzZX}v8}8IrNhx2Z5`Gb4hll$r+5Q@i*f^3 zHa*3xNqYUL;$KR|BWsPl$!vFWb^cml4DL5pGkH5= zGek`^ewl+8Dns(xZpyou>O}QBLgioU!Avs{I|-+_nbfjSE(6bS1*C<^hBuqD8njC5 zGLX&Qht8XGf@5H}S^KT>#@*ASpQ}lE7_75q=ZIbJH{L%^#xR#aUy!GAMOA*AqL7$c z9Fq9$U7wHMFDq-cA#NicOhESiX1IUcPok|+sN+>0b2}N&D)}z=lI{Vli3gND&tcFh zo6NhpY(8E0GYee)g8UZ}M~kJJ2eg@jR|m*C6s&)8_?LyhK*ogqOe^!HAXQF9=8EUI`zHjV)gQ_=4*4Ra(M&GsA^7HB!rF~qB zHlC~+!oPibC4F2ChcJULt((cxA~Xuk@%U=!?D%mc;&sDs;(2GgFymJHt4ITWPXpKV z{?`Ca$+(&Kx?=<_LM0QXPE_*@btYZHxcp?U>G-aXOm^b!Qli{OO2D06re@)=^6J)= zo1bKUSs&2&R~;7H-FSkON4EyQj}P! zn&UqTL;dn!>+t%AyBhTKjXUa=rmgkxQKEIWSxiQ{K$}hE8X&VbG#0z6Y}u~5&mjge z3c74w>J9Gq`}}C?Sv^}Dp-65?dm3bd=AoSizVymvLxU3Z3NK57 z-26Fb{%*}{w5WiM$&ws;ZW#6t{MA%*e>7)-X1gP3-D&h^a0s#RgH)AH#*fPdW%FrtSfe{9)G;`_?K*8%b0Nr~ud?}~<;We@$i)>yTA&2<_PlzDGVSVm-#;Xja6$#xoWC`zCiwmqfWxFJ#I?kBUQC07GBH}2yh>X2_uF3N1Z zG^rmA-S!rS)S+%6A~pNJH293=W?Tub*cONNS9b@*a)m2(HiBh}?|8G5at)>bjx@k{ zYzk5JPy5iQP%|+s=ppl!`UODX-jg4v^OpD;1{3JVb~pvrhf7pw9eWr(iV0t}S?cBm z97s@nC8vt5qW-$}zraD(^l@It-{WbP>u-0+W}jMVU;nDI@KoREnPY5DBIZmkywDtsgF4&|1X|H>u&w1$REnr1$xf7Ul-f!f zDvE=GP&IJIvxQ(G7)FVffn)^v`EYOoH(9L;6=ePF8p(%#eh3aTN}VhUcA@~PU4s=$lQYco=iUptQ(NqiIQa)|7x+_H+ch|mvP05j;z|@ zRQpbwg6~QZ&-TxJpU_#dO-;hW#39OCKRy{?LKHQejU`JQ3<#}E$QSCi>A#5}K^+e- z>)!*fz8LyMfJcT;rG`O=#PS1ZwvWsKrJwSH32lzqC`Mq=!loNY*9k%B{jL|)@K+`J zZlDkXf0@YCeNMm4DeF<9?$hCUl^^U-r z$+{pyL>^%KT(>ckzsbQH@w89qpC2Or)lUV@${)rK;2D0g&nE^T%!{oaDgH9}?ekl`+zsS!JIVX& zmX3_1{jX?R>TrZ4u=mYrqQSDjm*z>+>@F!|>7r(3@4cBMXPHIoR3|!hw5bv>qsTnf z^<$tu7XP+pIKhKlA1pA7B#61d==_<&V3-c|$ zQJfqHk`|wRM4&H$oWnZvUW}|_#hDI2=+<2ixMz=ehVjxf-)CK zT9CpQ95<8vMy#DZD+@4EQ80*ZYeI8}gw6u4!urQd4D67*%!`JcZ(4>^xYgt#alhJz zG-HJuVg;Sk{g7IHG4`CaE`=1NGR>G*I#BnEVD0&b`0g(#JwElt)Vgyj=rav8j!1&# zDf3BNUh$>vnv+XkBK%uGAC02G9bF91@K4MwBegednvjOxx)6 zCAP;H0ZTy$i9ZJ~jVk5h)mszo8A!_OF>!$QY%b00HI@B2-7uMp61wf3T?vkdcs6bE z^uo|!66uS3nR$JoEaiE~wW+7yM#ECG$u&wPHX(nhSAerpKWFhBGt_lAQM%qnV;pM) zT%nR}nr6@b&S4ZPg%PcsZ8{T40$TBEh)WdR_wa*x$ep&hm!{s|^6b^b!3go(hE97p z>KBYntf=6W$?X*Db0Z9zEPujDy7WWxPvWClh5Yp!!e5-qND9w45X!}er%IF#Nya$3 zVQlKc=Mt~F=DY>!ZxgB$*`zRwXCWVE@eHyCR*k5ZEz9cG;KRJiLH(AIS-ktnNFE-H z|9ZXWW5d~@y?9R97ef^%OzR)7D{}vX!#gHLCnS zN?>hM@I) zufku|KQ!|PePML%K0hp0yOYD}ABmN%e|$3zXlbV=PAMsK4-UFF8X$q~=sP#2cX;iA zAe#b1Q(od!>~}2QuJ4n+Q}E2gaL?d+ zm#GvIeZRS@d4X5>jzd89_Nwjg2dZRa%)xk&9lK4|QFnQwgPL;H2%(KM_fQ#W}p z2VnfT^WxecZ?Yi1R_|tBjgD1(@8P82uMPAj8=g+PY+`}hBXj_PjhFBPEhmck)sQvR zp45QE>#oIi=Wif0w<`3;?!U%`*MjMW#O$co`Fpkv5p9U3_dOe<2B+gk_xMQsp7%Zb z(&gY1rwxIlAJWsr^P}%ucsH%vjv)U1{sRF$z>kg_A4wf_dv0%08s;Wh%S> z(Ck~dT(thc+Fg=X6NZXJbPvSaE;mt;ik8m(6h9c!$gcouY!pYc~L35yjUq z_G%&)I{JPi3T*MQ2g1UIMPcVf^AIrj?lAcO=(R;X&oy5S`1E^JZpD_JyMaU5_UrYokWX3rJD6k?JlcxH(DZC@r(Cm;prXym< z4z>F^?W!XHLtJ1?VD>U6IxW$H^pB@MUVOT3ZL*4{EK%CCR-R%wtfX_4** z=?=*Oh6a&tkZw?v4w3GTVd(A->26R20b!&hrJrl~#eMJR*w3^7FZMf+1G!k&nl&rF z=Xrh>I*m3U5@m~hajDw5?rr74@pPNf|6OK|A>bUOcp0;&T;&*K8^;Q<3-S)Kf1xG` zVhYDrXh*VA(J z&h(rfK?xHu86mtEGV+S$C=>P#Z*p>o5RejU>Ahe6&J7n3VN=Gg)R$Ze3xb_J5?^9x zwKD0L(5Kki%V_x?9O+D>H+NXevt?VzJ6Y^((M}2dQBDgX5MUbNQZ^ zwiW{Hjcv`{5sv8Bm@FS#7r4~B# z;<$FS&7?5^jvD@2>O?AX?bGk&TFDtYrUTfYOa zMjYmQ@8bKU9gnHy8#9Gv)cuMZl?;B28`*E&C66)3Uipg7Dlr^!ZsG`k$wAVAPmWT& z9nJriBH$-Vesxe96vFa{B75r%?C~woFV9B(W-j~#(_e=ytS@Vk$t zhTty@wOqLPZltcf^D!o5|+N*t-{)n4{=;oS7O5*lu!GrLrDpyyJ$$U`A?s3(N zp*d@l6j}}{`Mrb{=!=9VTgartwSB=anPr1|wpPy7>6w7DZo)&V4a^Gv8}YVP(#32yC*MwnW1rl(vi$XBrDdLlx!UYBENbL)f89>aMWs%y`JHadEo~M^hnAqoe(A_1r-+}^u$7(hCSSWxy6&|%kCGJ7~zDc@HKVAr1deqsMk*^p-vtjKkJqqrljg}*!zrvHQT ziRhI>P{7hHhrtbYKZlS96KMu5WVHcBvj>l@R%2AzLC?~AanlcT;kv5dsX4>6Wdx7; z8X1#?RNI@6V{yoOx*b1ggcWv{Y*QX-;{G{_1=5~%@l4}q+iA}BPHZ`jVbQ&De2|a$ zcaj2#=lyPQlAw>Jgpz53 znl;GZg)?u_BlXTZ$P|q=gO6muKXeUPMKSzY^T5mJGM!bT_8~0-RHw(jtyj9yz!6Xc zHidWJZKX<6WG{oLr_~nHR-*We}Hxu0B5f z!f~~Lny20-b>9R7x0ACcX@se$l{L4<2~?eLlBjbMlo>_udy+>EDFcISazt4pEi&)W zmB5~C#IvVm$_1_^5V?X77~{iNT`wl*Jd#-6S>L8qhF6&`3)L0 zEhk2zM#B191!NtBuwh2ADT7@<5HDe^>=R42hCRJI#UPr!ht0=%QxFOpzyYuCdX-E$ zs9^U@)7(u>JaU-3BGDW6m60)6`Q9^;pS?)QFD?2=B~R+>GBV0#;EU2@5nl^c?Hu{z z(OwVUQ4mSdqdo%?Q&eWP@n`QN0qF`^mybG8;3|O?(v#rI#N-fjG^^~OZ|S=z)o^K% zxa=a>@0LslKF4JCfX{S>Z)x*~-2Nn~>taflo1v-5TjXcIg3^2ShFyj0`E|&lJDtA5 zo>PAvuQ<+JOD=hPN*}x2qB&2d^gbrh6*z^yb{3%NxU)lto<}$%#?9IK(RkD0Sfw7I zokWAu5!U_Hf~wGSx8qOWJS*fxI|F@fD!uuwk-u_f{i^z-EPcd`LY918y^8Pt3MIdk z3O3XegpwxvX6+GG!&AQTm=F@?UV?XCEYeRU`DW${LNR9J-wE5$a%AG_94XHvV5o~O zKAjkxEwX3`Wjn{@?C<3MVrJ3$&$uTPhL3ypmvK{l-sC^^F<{LJdmFg64;G6^?S*6C%D}}46%Lx&k zWt3dX2%7U|te7-F3==pYFylTcoA>Mx?z|9k;Ny~-8>r6=A{`SW%-*w3$>mml9M;HP zpe(r$GgH~Q%}bE2wV&mAqeL(}s3rSt;hMy0u4$7G<}W( zXnFx%*#c8peU97^qZpF~NZpGMEdSTa%aniPLFyJw7|CsYfh|eUu$(-wTb{ZbXZK^Hj~A*Pr~On ziN#<0O8VE)lC9y1(_Dup(swe8H-tumW@a4t zvqP3|aHHdSxTmj?fiy$EzsoQigvMJ?pq24ekS%iFnz6OZQ!U%F`J4or3z9Nrph4>d z&hK48iiuP!y_OJ^(fTizxNk8PNv1fZEmBe$z75B^ASKpcUX1J{@-p^+b$6jBnBDlG z;ZelTRx7&quHmwjz5t}lUYv4J^KrOCl1>cxbP0*chpcOafCTvna0;$RE{sUd&;ovM znC^2^d#n{tGbwRmirbl>Tpw#Fy8ep!w^pjRW>jowR#{5H-fpbLqub@=3)}pCQ~8+V zWJoi(VeAgkgf(%v@;aKIZ8ZJRan;7GuVRE86cc`GAZocnm>jU8I_+r&Hfev>F=++Z zW2fgHjP+9Q`6N&fCd7V|$mj!Uf zmA^wMi>#6Q-Mni6k_K@)Ki#zcKeGz{0som<^U7?P!ls=|HI4%1yGf(|tkv_13ZG2BA!i)|o#Z&;HA zCW|| zq|xEok>TD4k+wmIEm?DXA!R1T_xTyhRw*Skm=LqHEe+la;q>Rl;^XTX%I6xB>tET5ZqJd-}-{p7t5cPlzN91 z=yhSOJjH6W%Y?aern>(0=k$!ZMpcM&uYGLgdeIYemxl>y;A(9brmV_N9AbSbnu~g4n(MBO4!*KK%gM^W zQSi_p#o5B*sDO9PT&Z98l-tWh+6Xn&o!YisoH1OJ$qUfU4kBSSfA^F=ahZ7?5NE&7 zlsi2S-a%H!>T#PDDO>Hr|Mu1!EeIYb|A+x~-7O)48a*sWKNhR+$;3p>x79{XAFI#_ z8q+S*ZP@)e9=3F%=5U#_^N=;_%zX2-ZrzdCEFl zRXM!P*m9FX*&yxj4gS_ex|)56h)>dlj^-`xvMG zNoIP!S8~ej>O4@&x!5}ZfjrTF^j%s(P(%~@n7r}tGo z1F?-->u$}pyrIiid8SS`XN@G=n&*>}M@Z1SMTZInYD2d?OLlw9dhokgq5u*n{?c~!EWOZnSzI|)ru>@QrjNdUSFc?8{J@iIN^gDiyglUKzCuYF) zIM#>dI!D2E0C4yVjj};Jo;TFBPx+-uaJG~%S9s7i9117LP4G7&0a~taP76S$UmNCUj8$olqYO{aO%hz{a8LY_LVsVrT1-uSJ`W*cbkl|m88hU za41l1jC5J%S9t-Z;Y^t3vtb;tczlk~SEmlbMkMc7x>xWYal$d5jwolgRVpRjEL~}A_Z;34we0;dBk&XZSBImd?yhqblcN_ewhW<-iG<~F37gKR@=tN-yz~26`{@B=tm%5SgxB1uBPC@d#uhr11)Rx>N|-@dbE`9jd;A97^77NKcd^pS z&sv4ICcraoW_gX@?b$`PUwum2HxWnZ{&TTYd@61f?kIy}ZZ@ThYujxG5jl7x?@yh5b>mR3u2B*}CTAPF!*&(7hA|`8Jh8dxxdHkeOPwnPu*M%*4sMvlw6oOwOZ+XR-@Cq5&qzF`9GmA8W%g^eYmTZ z7xNAeaV~cdcr>Tn@l-4t$2SjWPmm#~cAdju0}*civ&_4Sm*Vh(o1b;3Y00$uW>Byc0T2cCKYg<(e6(G?U8)(w*D4JCnUHa`n)2?&Cr zYcWJrdJ&Sx4D3WU$?~a;zs*UYh8+9>;Gv%(2x51mZ$3-{f4fRU@+&&0YchWcArli( z77fm?CxuNpEmCt_Nbt3>^*;XmBawTg?eaH~)Am2X0ffM0l7%66JehA?9ohO}BPeZg zQ`{JV>eyS!$p04uWE|vPGYNh}B@^E%PR{CMILZdiISB30SqdGfxdMItEs0}Jqi&6d za7>+Bq%52uZHmrJH7+LDOILt0h<2XbBv}H)Cx5$^5TZgUnF}E~Leg_wD1}%(U1I0$ z&QltaNd0y3JXM3l!uw=lgy5N8VA?y*%&C^rhS0W4X_69C3{y`Lybxt9B5*%*Mk8_b zR7v-Fsu{oW<00pm|7{e%k8(d8*bty60zu~6;ZLU+K$AypG}$yjp(~MSP*Z)obb{Gy z$!C?-8Ki7000&VeFgER{6CKdgPvN1&QdW2<@oG(h5=))0jp${9be>K#)=`M_STWH?4rQe9S1CQ zn|E6|Ad4uOD`@@2jwef zntCtR8LsF3DOl3w3#U^s90h$oNsxdp9SecW2Kt!6Kx^KMEIF(hKMC&mo*)K16mTP704Z7^=FxffoG z7WIh{HquJMhxtfNu*cD+Xb?G(XYz5Bi2L|z zz9bE3v3REE*#b?Vf?a8#yd5O=tM*aJv630e`fROBn_R1vpE3x<1C78|bBvay{Vgd; zd+U5AM)oXJ;s_{p7++i_ON|!)l&NE0k*L>L(UKDV(jAY0zev_p9EmiS0o#0kojG+M|WfN3i}<^83ACBvg2V)i?MyF?WHGx19j2t2qK8P9yd@*LrwvEq$jYQN z`7dFJ_XP}_W^}aIl%G#8&?~HH<6ZMjI<(-wVjdq-HM*5Tj|JdV5 z?$iMhD)(kAHvYS3DQRKLF3#I3dGxIAbQ3r{~j%QbyZIw$tPk@U#C(Z)i(uj9ItX2E1P@wu4@qIs^-w=MrS7;73KIO@_<~2 zPueEJSxeP4Vwc7DJWs8f$}8)n*;m$`Dxc@N6aq^DkRN;EGidWSK6goi_+nh(vH5G( zKNnUXA1$J7{%1GWjC3}WKDn^A?is+ze5d+Bu)u2IU&hN;Ovum!{g-#+JhoPoa^DZj@Kt_1|4-jsM@f(&%|6Ptm@ob94}<=~ca#aUafg7O7xr zyJoHbvTRY2>P;@H4xKt>z3}^DLTpH>*eQJyzv0G=*ru>w0Ll6>G`MV1>mf(vqQZCo z{nOZEd%nOiP9E$L@&`eRmwxnW0uA8uW{x3}qVI*s7#RF($9szJ!9zM!w6uzvv+dPQ zb>}6f4LXys$2OqdGC}z_&o{X6rUrnu@#8OBrgdeu-hK6@4a1*h0R-okl$>v<&pit! ze`_gjPQqTT-E(6*g}{d^ja^ZxMJXXP6&S9w>US)A!=?UpoNd$W5r|ru=lgq_7yPz% z7plLJRS&mIRE=wunc@7K$`nSt=ak(%9%rnby{gY2cDjpZ_u*f9bEv$=mP!L@u3CMN z6S8qDzXM$PzWR@1v!NTr6shI4!)(@%jZKs~~9 zK##qXstf4hM4s3+B@T00_x(&?&gbJq3(n?QLeDpG?6}-Kr?rg`0%e4h2-Mka?Q}_A z0uH~O`wD9UfiX+*)AnI8@O|Q~)s~6Qb2K!SSHrzRUxlfgMm37z=00PlAX~4+#7WY4 z%(tJmJBLZsP3r6m|D?VMS~kMiXT3JY$RpR6qJomTj%Y^^L0L4#EoLJcInVWR{APU8 z{|c~u&%KIlzm!mR-vRt~S*H8Fds0LiKmTOA(XGf80Ji8LhH=DW9t}>jw^`X%=5kq| zFjT6H4O!SB3E`D7+E%UX2HB;rsAxkE~(mr>{PZIe7NJd)jV+#gis}r_w!x7 z9gI?LMaxxz=jdvl<$|CS+QHfkN-9hf(!wSfPzjqD4$D?@{yh?e6N130UU;kheNe=` zE3)=GSt@<5N`h&_1AaGzjMyZmBfw~i(J?LByE8G==zX7;E*%s0uf4I}g*4Qp7XUbt zPEpFvk7ny8z#elhfj{0%5O-prA5vpfl%wCEv6{=Add2huXBLLEc;-oKk4X-WVBLDe z`#XtP1^VGe{4@Jc4P_G*IwkB7w4YYGl{uLb;YzeuvU~uqBuYeqZf_$$q$lL3v3*?T|Mp{m$l;`NXz?jA~QG8x1j=y#hK(7ah%c>Z`oz zw{i$e!@U&}8>89^z^6Y`#Q5-@6lmqYb%k0c>kK9Lu}>^cOAICJ?F)5t(|{K=JIg(` zw4Y`Awp?TdXnQZZS?BWmK7A|I5Amuixci7~#Sohv?oFp8){a)taFB2}z|Bq5SYQsY ze3e#YF9jS5r+C_>Q4$1st&?pmr>Ai>w~!+;KZDCaMnTL6&xYiKWrm`DYSaup8z=?> zdEBHxY$bhwu|lY51Ve>8OJ_SLq+vRv->CnyH!Q&uam0z6UvY}n)~>SZ1$RGT^agyo zcy8^Dch&@~YJ>_IOX#n>b&LCFC3GM~Icbz0v~`M`E_%;d4VaSZO2|>oPH0hGty6<+ zmlUU@-OXXeQA(h8@xr_Y3A^;Nr@?E4ZjzYBRX!nM1I zMCdFduCyMQZMY&zV|I_NlaPCNZz6B=Dc%L#jv|-$iKaEa^^P zH_Y<_(ojyo2ev8-7zXG`u6Ub0&W~$JGAMb!*yZ~e`Ngm8gxA3vCiEE5dN+qpsy6! zL~TILS`6)uYYjJ8513;{^Vgt2icYNFwBcOe-w8h7vBeBkYH;2o-yQ{Hu z!C(YuSJL&nusw11^DQ*ybVh=&DG4NICv;{cA2d+5RtlUyBRQm7Vkt&%nFk0ZGphr~ zx?XN)xNyUbQmBf<#4XCD5-`In=M3MD0Oc_PPeS=^E=MCk6yn$Er2qzTzpLQ)yV~O_ zme`lIgusR)h7S~L)izP5@*&vBZZcp5KE7vj< z>jXHAF`g{6MaA>fSgSYS)-c>ip^?>pDx`@hiiz+r!`}P@>q{Ah$oI-*^7m^^)Xk-J z&A#CI@m6>uD#>QMuEb0OG-E7ZAtpKNOpMPmTlITpju;(kwmfUrob$0?Dq9iE5HFW0 zUntwl9qkhPiLL)li(xu{zsxutH)+hM*(zWOjaZiJw}-1pM*T9qK$Y$u`O?@{a3vg! z#Dd-Wgehw>sz9X7OocAaNI5z(V&$BrL66ddzAl8%VYnu-#_65Pv2(s^I=ZVhS`f6L z)PY47a}HRoxsw2-j>Ov!owh;i;6o^_)aJ@m{x!!x)%1rg@%;ZeCu5Haw(Oh-ddL+b#cN$t7tLX#MQD$)-_z! z+j6UmTP1bz{@s{THl9j=(7vH1QmJ~SSqKb(%4u_ih0h;^--^aweYmk#^`IL0z3@Df zl0$onbgD`@|D)#n3|)3l<-JB(x!%v-Q3hY}uOpXW{;He$8xQ8y<~);B{v?E{G*9O( z0aMS=iD0>hNlHpe6Yr!-4jz!hbzKO9tQleu_~vQ1_X6W`zKZ_bph1Ha4sx0dd{(_0 zerwwTtTwZtOkgt3uShVxy;yki8Vp<~KqmooMHT0s=U#KK-4B|5KS|xMzaG2F+mYdz zojq64NFl=@PBg;!6EQB@h&JL@CvbVT-t&32ZSPuvVD{gLn!Ebzt(R-n+F?VG*PJl{cFV)Y*UI21LJUgZ3``$}9t*E-N7;^>vZt#wPkFEVf`2hLr=N~_^ zU7qm8UYk${$mZcP`|JD-T?!2%-BD6wGE(tteXY-coot5pO(8#0DqVQ@g{V}I7#bUC zgH0R4-uTzWv(%&!3MDm%&r@&BDNnTRc9{xO%9T-fU~8XMPDFw#taXdjC8H~U)-@ni zQXRfw32QzwfY0@Rbt9Hh%;jO->DP`fsZw$jm-gBprLzee{Og&on1hl+GttWN0kKD< zQ?G#=p77%t=XV93k0_}%Ggp(C`w=!et489A5~?6ySTl^KG}VCwpoNXAhz;$?_R1`b z6DcUHvx-m?pHflVo>m^!;&|+j0cMEf#{!kiI{m5Wr^Vx=)-t_)-^lyx>V4wHLcxwx z({3{STR`D>7~R2k^{`)r$K}>~)|^mYyve}O0}?%UgS+%KH-~+;N~y*yggR?Dh>$F# zE|8wv8Mvq9O;sx!EO#!?Bz@plAA#p?gOQ_CjA>+42+c`KqkeimXT%be_HktsAeyIY z-|Ei4HIkV@B9oD?NCoyUv~;|{{)Oygp_<4E8D-CCwoAkcJJ+>^KOmm4!6G6O={x< zc*oEJ4%|uY=FhJ)V+}nY3B=b zXheZ_@9fO2m3)K55V{Kk?ddRUx~kv~Q&D$FQj?m6Nsu z>_sS|=dMT7>T<^fH8-#vn0>W%T>HX7RI5cR2`k@2I0swtk{3}#5rdsvCN@Oge-E}c zlc&KJMtG&XgLho@esTJs(UeT-@Rs^X@!A2UCK8|j85Ur|JB9aK-?@L{dL40X8~2Xc z4~Ln?!+apCtPgxAYwQ#u70T`FOi6zR(_;TzEpduyrWU)bwy$@ESkh58Y~#N8)upKM zRF4RN+VxP@oailSyPoNWhpL0Jky&1Gnd<@a(BoYvm-VZzLmZahoanHbH*`!PtQZ8d zb3ZrL^P)WibpBueU2K=4mh0zh2Yc-e6N1^|?!Oi2 z2{md>OIc&iJXJ?+-jO0cSUevCrYK!l*O162J1@x-m52r-leZdELWdWCdG(wm>-p2! z;vbB_`cowjhp}vsw2=)Y!74u_ttzi-?&4Nj_D48Sf3y1{hYyaZFA#;hZp#?#m>>xo zae6nEzy()`1heiaK!v-b6zD49huX3<`CB3&*EQi$^lsJFqDNyV=J9tL^*iAKlpcy%4=C77aI2!&r>tKiciXeH8fA7z!u$H)c zK=e|R2>o|9uI;C{z>`4=roSDgMrc8b3{P0)g3`#Lp8v%jN}K;r2+ntjVwacmX& ze`OW9^ryr~+yV#uCFP)(lJz1t|3>93<|h@}07%d*o_GMl8)=aHOdBg=$rL;V_#Qh| zz#f~Sz>aBvX3(|&;e1{BO7b=XabeMTe0z}pDiDs3X}LT&-0l1-Y(iYxTIA{KU4Zln zxcLU;;VFbp+1{0Cg8d!{LTFwq>%ZCkyfB9n8U0NmYgpBj?FfIi0trNIRmXtJTv%9U zqbb(-r8FxTL!4MkQk$7lk`lSP>L)H%CD%3ZdxWqr zr0>|THE+pu`c$^gbJL2ZQlXmIJHbeIHE+FMQX47pQc7l#onv;rtPGuZkt_$Sl5M^UuE0OU4IAr0uw}d!qbiKalN2C zPexoARrx+AL^Y^cAI^9kwPI$ZceE*kS(AEk$mc5}Z(G(hQ`EFB7YCJD$4&m@?2 zK++#=z@Ww&jNW}#b#h8og$2B5`PiQ!P&B<0p)MqMS~WY0C{|^aV*|@MY)@s1$`f#C?7|?4iNa1-mp&0s3`&bybGZ6c` zXPe|ON(uoUdX_)6`ZBBJ9BF||QI{=-EpP6+q3sxAeFA;wmWkZo$Po+R5&FOB#V_7U zwIo@K1c?YVAs14n2{I75JtEZSqQ-g9k5@BZz0K4XaDO zC*?rneay#(g^lsYdp5u-DcJs_#mov3q52Gbaz4Fm5YgCt7A___+u@GKTvS1-!M2Kk zwai;L2lZqqwJ%)pmQ$Th+@Odslb=omm|Y!zU`U&TSc&9fe`0>=3vkK2n3*%aCA_VK z5x2d~hOW~!`C2vcJD+@BSiG0(R>B5?vEU4=zLW?h(CyRF7OfAaXuy%D`KM7T&+NTl zwC)-HG!RE)G>!}vO8TKF_j9$^tt=-?sXKBb-$0Y~VS^r3OXP9j2DN3cR z^5mzVz?CF}6G=9ODtjDh6{o#|NBS~P2-bhpzykR zboG6-$n6JgV zR&}Qei@CEl_#o-q7VmMYPZJI`-}nNvPUb+ZAo>`r5@%CT%06cLE_KhStSokh&|4+s za9>hBMVyHF^cd1co7Q33ZPz|_M`~!+vO@%hlJ4QEs#F7X$CeLbhvJKc3WMlQ)fkG0 zFUoyb81e})`BqWr7r)Bsv=5*{{S^4+G|9mnRmMkRD%FC+6-v%iRU)e|I={7%ev@AF zBWNeq%a1|nvlmb~so^{dY<5cO_;SzyUrJSf01~9gTG;+|%T>Wn!Gtyt%KtVj_J*(% z_KC7H@kt5@BLqVOS05^ aDzlc(4;gi#l~q+1|Uh{B{&jRSpZC3>^**4og8^S{)7!2?z%VUxkVQs{wc~ z!T!R*yQ|Ad!c~ot@525;v;n99;NWT#Fdod1;NV_;11f9E+}+)6ZEagveq?3iadq=L zIy#!2odbT*W_iaQ6%&7Tb#-!bQeIxA3e;p|=HTWPvaqu4?;pIqz1`f}l9p9!ZR@zX zxv6hxQd0iF%*vIRlzM%A?dtAbTh}l#`A14dF(^2stgJFCJJ-wG-`K=5GwX}cdw`&@ z#OmL_Lqj7$j2rNhI+>FJsH zgyi;)PRO_N(Xnw)FFzMoPj*iJ-@hm171acV#3%nu)zsAK=otx%02&&awzqd=92zbtD3Xy=-q_d_5E4sBOj%o7&j)|~lAB*w-zX|B?cwRW zx3}*f5Ii*notd3GKfeeJ0`>I%s;sJMZ2JDSu*BKLgO^_9Ew;6HHa4|n=j1cD z`1lzVUQ}F~o{_DtsoT=h=IG=$FfiElqgzB&Dl#foOUJOYt9y0zZ+JuuC%0f*dq;9g z`oiMU?(VLEk=gO_iKDao*!XV~GpqgmgV?ylPoS`t*7l!2d;k1_{_5-R{nZCl*C{Fa zrlF~4@8IGe_^F|>+05LgyrRm%(KRYMuBYc$W@e6+wY`zCg@O`L-_SHWC-3a+>|0sI z-@j{6=yXVE1P7PE#^z>BY{JOMXkPx;q~x@Kz|XRB${ihDscD(ox`uZ4&XKe`^)Mnv zbCK6`hl9iJdAZ>GogtR6%GVw;x*i(N)*jwwZdPzWGg~JQc2yZoYCd)@cJBLjgeF*< zCM#V9YXt=sI8@j_NN{BENO175k|OMS^Pe(|h2WUr5&o%v1uJvGzkg{0VmyapzdU^i8zyFs`;b2UdhysTQYr)CQ!wC=m_pL8aAc6mVQ!1?d@_7Y&&#Ql0 zstXCiO80^A@aUV?BpLM)~CQr#bO`DmQv=nLZ z8>s+X8~OuPL4NHmx$8M~12;akvn4|K$L=V!SqWuoL<8eBx>>{p487`6%Gl2zJN_n{ z){hwr&OP?|CkJ#f=8O*J)o*|4+PE(>@LuxslNCtxT^9eLTwa>*msfV@p{3)QwoZ)- z`v2#5K=(9^Lp4jEnu;laiNBI*A3pA;=NA`KmKZIdt3^RRP3#P^EqW*aEr-kpH>B2F8Ng1-0xB84zajZ? z)Tn2^)~Vyb3<1988Z}n^f;cq0d&M(RBb@Sv6g=3YF4|Io*@4kIKgbv$2WXP*lD?^* zV0&aqKhZ5)^jcbCi7t3G8Wc^9nvCPoi;hGDz_q;SWi>q{s9I%Zg248Z7S=KX8~_C3 zA^Mt+ZsGz?l_8Ukdcdh#z);Y9cXS)o=h zZE*j;eG|tKIs$=sGdS@WPTZDy`--jKBqpH=3B)n>R(msC1)6hcr%yDKGt%FJEFBVg z{BlS-cS$YYODKw`Qz&)DqYEftfV0Oa9%Z9qF?R+cb);Je^xfTPpHa8v?b_Kzuw%Ky zTh)b0)J#E_&p)QBJD#0wl6aujkvy*-s$Qpx zG|OSKv?(~}oL?;CM-m0cVM+8+vBv`jFGw#i1zN0JJ}}fXiIvWl-|^hb^ZJuhS=yjq z9ad8e{5b#P6(coO*1W&JVk-5s^Q87JGX}5pZy18H!(puR9XtJ%h&OIK!g(NVI9T@H z?&M;*6pkB*!Y?ThY8a#Q`!kBHJajkk+cx!&?r)2BLgZp@Qw9bGCzZ-u1|C#SY(G_7 zJXA!Re-`-r`=elYjC9#|YD>yI{IZ}2y2bjNe=lJSk@eWqpxa$xkaOr{h(D&M5$~1E zOI9^?ojFSj+Sz9_>Pn}pG|2qGME2mvja<9d6U4tqIy!pbqJpr_DlX8@o?Q&FjDa6s8N}YL%sS(zSy8?N%GP^`cm_|T7 z#7BHvjE}tfqg<8gV(R4Qlz~OmhQP~D^LFv!*%Pv%A@`EM@qv<^Emh8A<+TMpG7Dba zRgMRUJ$o_z*vV%?El8eYhUo>%_A*b*-_r5@V#E5xEhvS0RQRc=SDxCD;kZkoF|HSr zS)h&UvK_v3?RwhAz=Q0s*`y#c$D&nFzt1B|Ka0Q4h}szi$zh}Snf$}BK(G7#_1Z`XTiM)BkGUs6Bmjn7Bfd6@lC z$D4SU@}{Xtd<8OM)Vn+KngCQ88C0a3S;U>v?cDxK)OMDiKuSIt3T%(e*8w8c`raqcrEej!3IzpWe zS&bIoj5hWL%>lh1BHt<9_Sa6n|y^U6K zs0aiZp0nO-mpBiO*vTyc>Gi4Apg^_BH?F|PUNps0kSdMwo zr<~4Y5ufX1gQe*q&AZV3*6|;|Q8q$z8{n-7riQ8fbgEi+=Zw6tm^ret72BZOBoZrp zXG|0jm@EzHvL6?qr3`6doqd+^ z|NBE=x4L@8MZ4kWDEIa>uB&zeC;hk$q$rP~0FO;nl6U^15q4afbiO)d9fV)v;~_ov ztqH%Ry>{5}(lk-C`F>%%e=b!eCe?^mCSXP32rn>&yshA{Wi%t|sT>m<%fKY5g{JHu zb0taO44ot+pHf`X+4evVx^lnU)ptoJcNZ8$_&!1#mWYl>RFP2~>iK5*oV#=9%nq5oaIA{m0x)8?MA4jywIsZVv`1v>OdmZX@Ld=$ z`UnVPUE^}rR>hP+aUC4^2x5o4+ZLEff^et4ExFg{$0!rtL?BcF&IK|Wjr++v{tifc zD&~>~V)&V2*;8dKg~A8jQ@8`5_VH#=js=|@spv#Jp#5l4t+hue*e=MaiuQH|wUTup zUtZF)yz?}CH$@kS!4ZSq_^jY>X2~~o?j+IvY8tiD;#6U7BfJoerM%oqKre{U*jhk= zsvK<1<7WPe#0N$M?zTiE=(j1!l@p!W4X14-l20H-;nc4&(nx&902VTL4H%860hUyq z9`Q@6(>{N##c8#dK7tq^7pgM* zML_P3u(G@Pg?^_4mTWM$#iGRrH(QCyxj_3!61QaPCu;u#B&})~4{2%E>mKRGB$EE` zNz!EObCXi>RT3K$|8l&fBc3vldfRh-ydE~8GK9lMK=ClaT`WRUX#oMTu#h@a^1XxK z@!NC`Nlt18S2>Vqf!b1cPR8vKCQk}sJ36U@as1`;?Ebf0xAk${Hi$5wPt*6o0c0ng znOq7^w+CKTvYB7w>79qnN!^cKtrm)uj zn!+0NGG+%n&D+keS8X1XI%esEArGLsP;A-Nin~7*pm?pzobAd<@Lw=g~ur zjU~=M$JbSgP$I zmikS9>2S4JUr8pC!xTsn^<`8IMnN$+G^7;F5Jd3}*m15cPGuD2ej>?%P~Ld@`{N5W zmSHKPq4#cBbg4Rwe*n4%G_aGbd^G97bN6BLMcd<7OP!GQ&sd3kVXILqpkfApyvwM( zEPIIh(A2AFc8FppS+KzD1V`EZkQdo2o2E>r#QVFbPbV<+3YZVmbb2G8bQOhXJ zhR6czbOTfzW)wCfm78oeMrMEjqAJqC8kHRN0Ja=@{S^EK5nn^8VVxM?IG->k8uR&t zq>0e9!>0h$wQQ^Tsc7vd?9|v3sF;U|Xkr(^_ewRM2^-RIWJ1B(DD zM^$|}+t9xzk>gvvL3eI~f%y?cr3O+J{P{y0ERYrzwaz~WZ0EuFyXAv8zGkAir#6vg zRd_>lv4QWG3^Hx#^cvvEKxJ~7YvK;X4(D)B%a9t7sd?}kmOcxX2^Iiq#FK)je9*Nu z-mMcLn)|ulVcC3b;rU0xw+>r2lR_k@Ue7-JpiY^ytSrHh(@*g`0rLIcr?kTpwI>Vb zB^_JyXD4+|QnyYrQ1)5KcE7W7OK-xkK0e>pBk28bqKMN=5&7<+bN4?4Sv{XirFtX)|l~Bs7h(J zNYzo(_^FzXWb|C~y2{I8z(#}ljC{FsaByOdo?8eC^qV$*VVhEFT)Skl( z*Ow4@^~0-Sgb&+(>gv4d5!JRgI%zjZhNo$oRClko?~OVK4pvY+6N-P;#HLpZq3-Qs z4$7Qu+4AhSXOpUA9(JcRE-KRdXiVv-q49Lz*UWof6Tou^&{yYCikcjHb+%A5!M!XO zi?L{jAmQX@DZcFT)RN%gM_`$cur151Ub}SkPjkY&15(FnnCv>L&aXv?HRA?;w_*eAiRo7OmnTdec>P?{ z684~C8cZpBpyG5%(AoN{dMC`C>t}`!Dsed{u>o~arcpXzqH_B}Cdp<< ze~sq1Dk8X;oef(RySbc2>wEu)-TI^nL`o{VV*I^lP;pOigqS>YWm`!#V5rzIRrYi-w5`KTr_o993+sA|03e!+C zMlEK(JZIr*x{6`6$N;fmO;XFwoBCI-nPFTs+^#1b&W?$qrm~_KZO^VRr!b+*IP>&0U3CKbOc)RXXm2y6U^ulzG_@cu{ZjYWr?c0b91GLr8J0xbrL1wrhXU}#=Lt%IAE=n` z_?{d8+* zT$OCIZE_yd_~*nR`M$0pDVv&z7$#_y%9z{La8E;T%YNP4Ho zz$xdSb7 z+~br}!A@d4iHa$6WaiNWH=ju3q(LBIJF=(Y2t8R$N(Tmc;H@_QSsC^^oHZ{#8afMi zymBnK@3||8JCohfEb{-U|1h5bf0C@0ppTzp#fr}RV!@a>)w}=ju)eau{vanU7f-rp zk&_3U+L9~gA? zu6H}s;6a40-v`-3Vz{$pPKACML-QwmQlE}if^umVOi;8}UJpn> z7hF4o1?TK@4px=DWUYfabobBYl8uCe&J1)dUc)AI?Y=+?2E zS8^cA&I+KDjK1?DiP4TT|@5~`h$uoN5?kiqV zQ)FJGLAx!E=DIu+Ixj)Xj+?JDv%4-^SZ93md^})lZV$j^+(>Q@b}uXV42xLZT833c z*s1Oo?sh+aCNWl61-n)c4-QzT#b*5Id=I*#&(qX~yUi4Z3pm}LzZ8-kC`8hdwsSg211r;j2el(uPxr(^?9)waoDZ+`64f-EP2k*yx zOX^#f`HnlDfiGtq@1(v2|LYrZBA%$qg6B&qz}WMr^l1Fvcy12taY8yp&-5(b09G#e zRwVdTWH3+&pauG)kHcRa&iz3tA1D|aA<57pkSlP)5#3fNP>?-r-zp6NRNkI_oxiZ~ z1a@T_SH`asJ#Ae@ztB!&+-+(zrwH|(F{F>}72~o%vY`VgajoF8e#sAr zP^0dr>uahi#GN)-?8UH>&8tr(KXU5gwHLzmi786dWc>9gUw;`p*;;56bB&318AXNA zlj@u8R+^nEja+|E@BzEYza5c_E@B=@cJVANw!~abmS>sT;zE_cGii0j(Qmdadz4>_-7Rfc%<7o`q9mrW}nGMLZSK!#E|RapHug1zo0 za~m20W28&!`>`Rf?N11kaFl+W^r_V!i)5>0)9FHo1P zqpbG}A?MvK3PS zYyY=NAw6_zm_ydLgKaAJM4<;&r0|V&QmpUtBbD{%#ymxh;g`9ekF9MdX~w0Z2#TnS zA40jclMGtc35~Y~btm>PSlS+o_w{n7OHa5Pm{2gK2QJU#lH7L**r$VdF#(rRFg^k@ zd2UQ6nh3KLRA*qY`0_F+2DgKVJk)W(qpn~0aXsgsHPI;j$r6!NfWAf~X+c;GXWC(* zhHE-e1e2FAzTu+I&TS5tMAE7!`zCBb57{^k6<5ex$t2(I^St{pix0ePtdouyc!mG# zu$wAi^0H`CPNADvEIw_Cj~1M6`7z8YEtm>Fe~k!0@LN0ELpN#OP!7MZ{|o8{eAv!s z0rYi7%o>`ASwNZ-)-SQi-*B?hOcHsQy=c6r`!{}_fEbRc{c$PCRnw}kZ+rvk`E_!+aJ`6(RtENLLsai9?#!Ka{vuBj!kdc zsGZ5NL82A2ZRY$SsS3Y0IodJzbsvx;WVq`*ZuKOfnoHl=4KdcIai0s6|GSdJ#lz## zal{yfFI~>VoSd^OPW9z-UbolB1himQ{liJ?a6^t@LgtLGZ4VPKeaZlxv1|+X`2nM5 zK)Y0suxf~UK%{q$06M_RJB8wrmT1Z`4>wD;YdF6Locg6q8f8jX)`xELGT@^jLz}&< z1l!4}4bjNZQ_>MdAyXC#+6sLERhj(p?YcDjF4bESsRJVPKpsQCUjwOSakj zQ}V#Iy>Qo~q+r+NJMAk^9vl!TQ||03*X1djsGP$qpMifi$GM~?d(*&72~@$b@ju(3 z!?3Dftz^6inKR;fWv77bcz_fplgIzd5=fx=&noG&BR1AYZ>rj)2{|g_O^tdQ&f`-> zh>Y?|jb&%<6|olkkiz2lXQ#l1Oabm{%L_8E}F`p#^_!+KB_w_g&EwqRMX?Y~` zx}$zYGT!v%n?ss3Z@jY+;18C6-sM~Ji{zZ8!Yp1HSkU60!@u)EuG~C#?1vuzy7=i~ z@M?cun>FH8GGxSuC$egEaAI0^fQ-FGPpH zzr}}?;GTfufLI1QRG;3Hc0mQh7gvxAF6$XWY(Y}=!*DU%OF;F#;&7!3uvEIskBkjc zgm-7jMpe#Pe5i_M`D9aS&~ac+oq;3@^pwWzdF`x-Pdp>FQd+g2OkDW9_e~%S>c-7~ zYH8!w1KNI!$Y|9ndkwR552cuti^u;pJh3X8;4wWSVzIfTe=5|GrAs&%v-x7VMavmy zP!FDzoam?>zntJ@*VAfNvoS5hL8SF^*-SxomGg%IUBemQQDUNe!-%w%+$l}9%LibH<10bK#&7GI`pvwLvLkB&@lvR1Prm8bhXbhL z=c!xMx&j)<1>zG;L^o!2#eD*kj#o#jY2(y~NL&Kp9PyIc_oj^j*!Q=Sn^Sn@TF>Y- z*s1^6nR=#aknsF>@4qEe>%rn|7b7;})_k@2^Unf2en%J=4m{viF!Dt0SO(+YECvPz z=Y!q85jWa0ys=Wm9(BX;qC`hMRx10i*0(qf;{OdcI_p z$Q+4wBlM0Qd zfB8gm>MW2ZP5ZMUy&({0CPr)q&7H!lx9A2=PU~3ESy#|Y{-jJj-Mo)>iqHD9qd)+Z_-2~ zYO|?`)9q9v&?Kff-kB;dNN~=78e_-d?#k2N5s;@f{kU3DmUH$2A}flE3^PqZfzLU;2N=RGCIiN0RXdlNMII` zB=b5BQv$DC^>>~t*zI);m>F5n8~m`t`r`k=_qnEIdIPKKgxw>E?i!5^BuR5cMj|3L z0RofI=ucy+87=EA3HIis86;kfvAJM#vp=JZ!SS!qTKkxbljHRn5xN1*CPTAT%-m;vU$ znt$B;MAli04WV)D`+Q10KY)tliCL!Z5qd6QZn++6PNeD1xBd{-dA%WT%*QpqCx7JQ zOOP9TYWlV!{-$HSz0URBZ-bZj=sI~;An3RL@oe)}*c=z+q?CR`S^$m*Xhp}nJjup7 zd;#{U0Q+Ons-xtI84|#I(jgzQt^4BGzYOxUFUXA=ln5!e$v51tCe)Y_8I@!>XAJcon3glcaXP26q39R z%C`7QHj&F1?0Dlylq$0AZ$1~`+P3~wgx-!2VaxZsELlGM1%*+gRuZX+W=5pwXS`OG zCCht(nlmRj5i1Ye9}Uwm%rU}zoOLfAMCHkN9-5*@{6)*ig(IPLg!8#EIRW*B71Sb2sQY8_Cg zB%r$YxAfBP_PqnRFY}r!AaBQrGp^89>dQyobY|6qqmAO@=A=OX;thVPH@Vce*f-2h zWEst{Lx^mf^aS`U(Cwu8v*FoxB3K*5L{EtitbW;wa*)^di0Ef)^)u5IfBJDf6;G(? z^$IT4p?HSZ{wp3ZaQJ|*A@5xYN*VthBl8Y47p7ze36H&FG;!pUyM`vL*wc8c_Cy}F z=Y%%TsqT;lq5KIfcDq4lWgB6X+V53eGP@=Jh99qSRWj@LC8oekS}vg`D~=$J2Eu4K z6Nnrx=3+FnWZO0KzflJzG%&-n%}Tp@ftw`zBNrGU#>NT|<{svM2RBCaq(aodOtS8< ze719+Th(>~LP_`|LNCi-WWW6@`%q|up}<+~uh6GXQU5zrBB3HZ`qHTY-w%#=wZae+ z<%BQ5{`bQ)f6|S&0VS2K2v>(B5%fpA0wz}ZV+ac@y56v5Ww7{<&Dw|fcD`(oku-B* z&C4Y7?4A<G@9MulxFq~QsB%1Y{NWlxO%sst!zfC6xdaT1!!eku#F3; z^=;h6!T|5$i%MOmrPk}|C}KlfbNlZgdX+3m2SX29r8%k{oj>x%lUM*4rl->l3%6Sz z*uz`#5JZ1k^Q-cPgYoOcoz9f})?7~8iq%2lg*N&);TpvWarqs;4lEITv*m#;M27Ov%@GW=Ek{dh2Q3UxthSz^3 za!MC!4zJL@I$k&EiJ|QJ+YXIPj=gD~GZH`j{yj!kkMCn9wzDYUt9yn3t}=jrK>PYQ zL`P4`l(8HiIj!CH4(1|YTv+8(-d3s9Ag#i6{2G)d76^Kl4-VHyTRuVFTN#fcH3HJs zPhc`dS#A;Z3n@QaMNg>Z#}XcfQC>w#*DJD|Fc#C7}C=d zLe~)_1yeZ62vuA2_{?qr3cz6s8>{z&CIWK-a^&fHzwRHxGV~HwT$b&6^S*^;C>##_ zDjVApHb#D5?nX3qfRqGOaF)exp@N|oWM@YU4;ZSmgIuvVk+gRBWk{w59C#5Fo!{{7 z3RUFp3643aJ-E4KMkv+8kPoFWvj}`w@wQnYJj?bhNloNw1lIANOTrUn+s}!H4O-r` zCb0;q1PH&AYM|sxGd^KHEKt;Chg}?WFWR5J0)spvr(CS);nlsd&C2(UZXZM!k&{@= zhClsIb$7sPGO}8G{TW;p?zg-ZrSm&(GQ`N+(gYJgk8`SkD!D{&1UI44_GiT2|7%a) zYX-p}m=1Djh~)V!CYTY} zJ50r}Hn+6354xE`9+gYYpGDM~o7eky(nDkhYIy{MY92Sq+YDy!AHjM{s`@U%epmt+ ze7?A=RrMJZ=L0MyeBGs~KJw)TXSG`1Qq-{s8@^#X7&$OrY&@y^o`90CtV~;X?v(72 zfy%a;RKU@rlPuJ*%~aS7yd^lseX9j}O|YH>Z#`t?fX83&`gYI+NI)?eVxr#$QY6M_ zsn^ZqZFi9nNn$9jpWqOn>FI$P^WTJh(P4(;JhdR#kE61Zb8T9?5*T=HgtD)GdmlDO zy~BR6hz>yI-7CR6Yn$}tPsXniXuY%b3{G=-aQ#IaHSBS1K`dpI|J_`{Tr^ofC&P?! zQ)7E=U!bN()*tjs7`*OGNq;!rm2Tp1{=l(7Yc+#kC|bD0N0fmg)Nmu9r-?IGRQ~Y_ z$@1-=*pFjv3KM86%K5yRoyh?L>{W&hW@r*Ktif-pzQJTly{JZzT;P>$uTnR$8P6NW zNdVTeq^K0h^*Ws==AVElX=}bAZ=Yp3Y1)9l*mie04d-j(mTjFU#b-HrjmHw7!EPGG&kha;r zTb^``c=;6uz`!^3p&sqogL02mAdG7 zJ05w;1V$lD`M;Tt-wq*FX%*F&_ehEwk>vr0+6VqX`3fm_Foq9<1@Y4;(dBozO9}UW zT>`J?x>D>|jfN_s2-smLV(_JzFy#;BH!v~ei3qY26lVbKr5@d~97Yk;CfV-GxZX+G)ZIc!ejbu z-s>V}pOJ3`&O1izAv!buN%SfIBMNB}3(d|W1TimIKcN0~QjN^4+aMa7XLgACE3t?Ou+BRv;4V2hz#P?(wNYX%zPJCOXw< znHW%N!b+8S%04tUW2phg`;lD0lo;?CeDk3Y`d)m{2Na#A2-LEpeN^OP%x(8KsY5bA ziqh3Dcrvm@iiqU4*stvP)+}ObE|Upl=Tx{hKgyKnQ!TV7`ckKEs(d~DQ5&(>!NP)3mgaU?Nx+vw(!D{W zTE6rjoUyhV0#$$X_%w?uwGntv(S9Ro8V80s;2|w$YUS~_T5lz=Axn`z{vo&2 z^8h>LwNq0T#S_d?*&h7orQVzH`QJdkpC=qQa`n199?oXa=n=Y-OKLY+pMNK;<3;eq zmDymI!XF2s6y#qOSbmxEhULZ8Df{y`%-?lpjje@LKNeP>je9a@^LI>9(C;>N4lK7# z3t$^2c^Nq$Y?WP{_L5nfPg5@#)4!R(N)?2{&w@`ARMV)^Gc$v(+ z_)e4I`&ktHlXazpP))e^ZFxqWfYNBT#Z5NMne{93Z-5iQJr*FXJXvGF#AHs-K@|lI zlP2zK5f3N+lO1YY=B6<(y0T&`GIxyK(vb>R@as>D<9n1!5uJXmwb309>3-Z&DNZ=h zHNTzvDbFQ>Ci8+nHKCp2%_ks%Ulwt-5E#f8QLB#Zt7pBaPhI-0bsB$27Vxd8##C>5j1l;P zB~f42OGU72k0)h=Ac^AM*EHP~CW{UF8b3+bO~EwoUS!|%hO8_6cd(O`uM{=_+VFEH z{OsHE(tx_`qjD|aX4ys&c>gi(!gD%t2IH;P&nO+syhVMLm5D9qUOY=$#C zu&9qr5^qTzTl^%wwbAc4Uqm(6-ew=bAZ#Z3H0N^(zV?aLWwjUd{wkPwnuB(c(@ENt z*P|?s$iq1jN3ZP17UBj5am!PEsvW;RGPXe-_{{FvPQSsw{wyuE!$bw$7xakt66G;8(~ zU(vcqageJ9dZX@@)e-VDUNaLZ4Llz)W)!ie8x`Hfb2PDr?1RZ}Etq%!8&*H?!}pcKkB zCs@lXqCp-q@_bvZPL9u0lH!NEhG-&znU~$E)t+vRL1a|Ussr%!QlmY0w^L=Xj<%5u zSZeMzvsb|+nEGLtInCEz!^#0*+kv;b#DL!7(yDXcyvLC1(iUEzMc_2kZuZrS{-oPE zPPGx&HA)uONo3QR>5y^j{M%eqq|_)OCUH*6UGBwE#hAbagOFYG zMQq;lmS>+Ay-qf@GU*&w7Y-@gAnb6x*GrDk@SRzqR;MQ~^`09oq_m_Iu4W3g>;=)o z4*Rz_Z+Q;)UB2zqTfgMigfK4GRTGwjUXp+Ce>Hm5N_c9&WcdKz&I0kTI8BgVl7Z+z z<)Q%zWvKt$=tKQ~k(a|j*reRngnr>wJK!{SakV>h^JSfZaXq;ys%Enk50Mn30;HeS z3~p6;B+GYaCn-xzWq&bVNba>r%7J^YVf(4m(`{ zV3g`*AQfQn7S2oFk!p9{Qz@KcNp(h_zZRgfs7G^>N3*unmDb>Jc9D8or3}FU7F@@k zyVjjAod#dL8Bdd_xJg64JG3Ma8B4DTUSoTW`r-OtzZCm)D+gIo8zl)9vHyzxs=>+K z8`^S%lk}dyv~nGdjYum)mYrG>lO6wO9*mRhf_>HzK83;i^_))dp23Me`2KBw^y8x& z)G|@DWcRAI&DeATSDOci;_O->=wBqnC4&B)yz7YnMbhNLyQ3|J{tk|bz~WLj+9%mP zy{jD=Tsra0E4ilypp#s_A>;gk=`&E{$&*V*hz zI54Abh40Z4mTh)GvMkc{h9D;&4$dZhzZ4za{)RcQFx{`Md#9I!dhsBYEsj_*7fck1iRK<6bKr?;x9?&b!m7xKk1c%{*@ zmvt5AZC^m)w_~^RAmGeQ?Qw07GF8mhhw|kI6&g@$)-zoDF)&RQAd^_zuX2>#G~xL^ z;g@N3m(mv&i0jOwsXYFa8aHS2uftBx&1*A|eyc_<^D2XxAb&M~hZ(%)oEF0qdoRCd zTVwe?xa$)+(@w-%Zv1-tc9`wS|0Kz+--o4VD614N?VV#t(tocZGS4Ay-K`|a3WbH( zXWR9gi!s`JHvEMw=O_0?24MEvkIqNBrT-HZz+B2xV=C2pSBQV4Feh@t?$O3(q#;5X z_D?Pd%t-@gsMNh~(Rf8)F1)nYM2C@-7wdyGvcz4!;FyJ>avT1*7_lw{81) zG`m*hTfhJLTgfP&v%~H@p9v}pW?<9SkGY=euHbgn?3)=C^;~wi`y174YE1fJ)OVUn z5KGCF*?vR}`!Z%QVzfQDy%d`T>Mn$(MC~5hYT#!netRs!$yQ}N#Qp($##xY6|F3nH zyFJ8-N8#G8-xl$s#~0{Se)ul1G3nt#Gu*eXJ!&bkEaO#-q%)S1j(H~vPa`6y7qJvn z^6r}%Kh{69cg{Ap11((i@|zxdmR{@Ps&)u zN*%EU0!2Z=#efJs$D);Lewem9p1W0G@IVWbCb6PXk5&6#$LRB(dPt3UABjq!qTYZ_ z{OxDm&FRGIt+cgU>j@*>yKUno%FW(SP~f=#)G4|yZG4{s&a@B5=X#1rZe&Y$=X z<1T=2BQIj4QEU}87U>*HTgM!W_v=wvd=hQ^0@3A35)p0!fOuo-uM#(v^X-(io=G14PfLk z9yQQh=|c)#M)xMs8qquf8ZBx1Cnp1m#pp__lm*Z?(on@YI|#UBA;bh0t{J%Ddly9_<4xFVOm;ZAvzXVn!EN*|4*#Vv=Ea5~jZd zMd?3S{2|^fgH37gTMStK62p2(&p!*~;rzF-rzmfe0rFG`umwi*wRl}bVAySdQ@Kx6 zW_6LgQOX~_9SWBhlxaM57Metk40(y(eo^3MvLez9meu_#HG+YA(*y!Ai=}ss7V0&R z5_AffXHWt9qF_O8AOV%qI8bMTSrVUBe^;0LS0?|acd<@`;^lMG>&kK0`P5}XHbFUt zzKHRClV!SW!&sj%TumB>{AGo%<2#t$8N?H}dxRk+M-SMXak>4Z<9?;17KQttK>E<$%{R;xSPt7mE_Fzp z)`95cR0`?5t7tAwbwIL!TF&99>DAtDS7Ls<|0f2+;B6j#U=J)XUE)GCvJ3q)@_YcZ z9*iviPNtZl+xZ@3JR9y$Q?mT!2unKo-K_IsSDZ<-`|YWiiPMAt|7(SCJL*b0zH47q zIrYH_5v#BCf5+D0r)YdqeMj*OqL%M`NL3R*GhU#(1U}1`mMG%PMy^Kf%A32U&g8Fy z^!XEv%%5!BaMBulZwX=0H}9C;<{IpF4NfiP)XODnQd{q7%-^fLL{+R5g95S@1SpN!)e!l~acp>1@c|5e*pM@99${Td(=N~h9| zG=hMHLpLKZq%gG7eie{LhLBXcV*rt1=$4k0ZlnYh=}wWpd;He>TlcQ_z3Z-Z|8gzB zbM|@mKKnfT`NY%fecagk6NaS@;ZYij!>v2B!Z~KG&( z^k@2{t#pMY3t(jk;?~k1$C;Oa_-UW zmXd}xDNYJb8R>fhRpK&L?pl(A%o4iffh1+VB{qk%tuOfZ?R@>9597?}B2odFdB4H3 z-aLHWK=cO>>Ic!FR}uh(yaAaRm~UL~kjPfl-A$i8$UR|S283pBYTCmS{xFsDclBx1$duQ$Yc(%eG|7`nQ60 z*T#&1lERdZu+tp<30zX0mSsyT1o$`Y8HASdfYy?^1_z-MT>oqV_XrORO82o0b{x{E zq1wL4BJjiaNSv?@qX!YBM-HV(B+~jicANmOmFQkp6#LhE2RGjt$R`CLR8%xeaz^Ez z&r#sx;n|8nq0~RLf|&Ve+*=xC-t8p9V!Jp{8q6hWn`a(N38yMQAYwDQy!(ThB~b-g zklNfQyJn4=Y4ipr3g_(A!(Y%jb8bag=IzDzmByn&Mm7zsZYe?FPOB%CmaiU`t>-xg ze5n7IRGo`M^7$zGeE)XfcE+o-8j`X~V+Pbox|f2Oz|RGtg0+>ag#n?z!al1Y7xeWX zxxi^Z|3!{otfK=M>%-mZMF*n)LWk!5U>KmRV5j?okn%Iq@`S**_>)TQ$b1o{+=rc8 zxJ}eI(a8iO=%ss;xrRhp%N`0bX(X!j?xR40-X`_YTZ(8Uzpqbk6~|>bi1)Wx~5{5aL)mBVlreuF*{ z%@2Cx5&StS<;Lj-d*`(2puqe@Av4ha8s6Zgh-N~mU4@ATcb&qz?yZB@x-zGP1A`W- zvZ>?VE~$qS0i~@KT(!!-c&As5?1@zuc5b=CDDF$P#6_OTRjzGYOKNUdpW`%I{4(}< zWS^Ag-bJayL;Yb)fOwQ|SF&u`h+f4-H$>f$lz#t~aiw<+=VQdllJQSc+cqYYVP=05 z!D(JhbU|P$zb!;K+H6$s$LsV6wm{)CcKh+YQlim673^gxdr7YlaOIuoiUZcih~Z5M z@~8e+>D$o;f5ifwV)?Kv1wk{eKsWb~)I1YmC@37~nm@qzy3uZ-uE@oUf1i5XWyVC; zeGVwe9jjekj3>V7n>Vc&jbI(_y_t>6cl2=8Lm|K1-RI?I;biRMOTn3~OZd80^@V>i zSsNSwPq4XWKUZks(V-$Ml=VQ1boAfzP2byRy6Tt+{sRzmvEO*sDE<%mniuIv5@fM& zJ9392IH2R6>6f@@*-FRdQYn)A%q>B%`Li>i#OEHMjH@xScB$ienHgexGh{B%u{y^t z^fPk-dus{$)T6~^=Beq*_2~VFo3}J1vu9ZT#kmX8yF~$K*0Vu)M?m?w-#oZ4-^Hl* z)5DDn3RYB8hK#p=FS`V>W&!K44Xg?EURB{pGiMlKHe9WT)=`syPnnDf9qf< z%>UPEF_w)7*uy^=oDHCup*My{0~GPy9rNe{85+U7y!ZVzl4DAF8;Tidsp_) zI9ozt<&6qssRC>uUpg8f(~*ps1~~IM^yK~=smW8JZTpQp3*yRWX}XTH`yV|$*Xn@; z^VVT09Iu{p{aL0hs`5@AmXq6c9+Wh@`780(u+2)Tx)*VMh+4__;(bSn`?p3iZJCdI z@(6lK3{bH#vovh|3+!jf$*_B&M}HH<^HP_xk88^l_2RP{24$rpgL;3oEv1X64`O)S zpB&G^l>LO53XB}|TbzVKJ}141(xq|ueI{EV1THLm$0iMA`T-G^RJyn)LQQwmGrOrb zf1}hm5o(&Nkh111mo-+58hpQLgN-1dv%*mf z`+1=B5Tky$&b4})uN^(s$C=<@{6qYnp}WYz7k;2F-{pe33UTbJh#j~6NUM(9Wb)U^ zcXftXsg%<2QmU{kKWps&DK|@S`@!7zKG7*k)s4U};AUJd8t=1 zOd(s3fUkA9{0y7UqRLoC*s{o0s|;Z?_(H6zlbj_BwE9T^Tim@Q6r0h6@#ISck&;m? z^2OsK7TA-A$`CT!86lyT8xp{VDga6Lt}E3vx0zwA&RZ?(M;04j7++rWo?Pb^4idyhqkAcRtRipCGprh~UC-TETIm^PW*4*=Nm6_uHxgb;M zX{|8v=-^mBX6mLGgfM|oX9-u(8hjSev?Z3={jQh1%mNgMJ~iKM%crEbHOl7#B72mxS~a6H0@1LCKiPagj-nc8Kk~ZEkdnS%{X(SrtX`&O zGllp|0h{=OkkXYyE^y?(WpqOGl$Vb0xV;-zn8c* z%K$XZ0bj}1+-|W8U+@BI)Q{-yK#0ZH|5!nwb=1O$&%JQ%(r&Y^nglp=pKoVDrR=*!0c$$%oW8@ zIYDlfMfparA>Z%(y$scOv(K#QI>3Rsxc?BUKZ{jJIFi+uRyl@S$Y16MSLs|SW`r>l z>4=M~l-`cgV1iiv7$9S90g5NZ39`{j&0kNw+O@Fl5op%G!<-?3UAe)wy1za5c{=_F z6G|cTi=+^s<=jgJcLrW6k z|Au0xARSz8>$oSG7$NtRxu=3f7QUqq!o!ZkGIoO9GXZ|4&&#foka6jDhJD!KGIUJw zQF+%(e{HX4&jQ3Rd1Ww{Y6bS`%i?%+rcusHAp2KUFu1rw00_f3_f9#(Npvm<#0z?y z{(Pm`DhBvG&f@=hr%B@pvKvouxF=CB>0OV{JG*pw4u#Q6;R00;a(?;mZBAmtlBBar zvf(TqR&{)+g4&~>lh{CYpO}&s#{n6OKrQF4^T@{GSqO7jaeC8a1J8gr((ls{k04Qo%6{WcEBqbQ;Zyzy5x$g2by>NkFE!&h-7bQH}`1mQ|kbp zNGad%R*Yl{owyz!2{HvWBXKo@9>b5Xs*0?I_aS3R_CVI056RmQej2`uUHR?qz*h>Y zTKNmUI?fzR`Ftz6-kTikCXC&)W7bO>5$h6({;fXLo3l)POFS!&mqY@FPz3#U|5Z|qmq(BcjD>rxT{WOs#02i?bvlm>g9#g&^3iIE=e0=Wmmp}e2?qF+GCoSa1 zsF;YHKmTC z?=A9duSc>XS4ppQZciJbg|@ce~Lhvy5u!{id@*eOdgk zpVCU7y%3hqjA>QoD;{m~Tbfa&iO4!$sV;(u+gw?Bv! z8w^5DvHi^N;x8Z}gBv83&jz#pjziky?4q>DDe#SJ^YImbXeNBB<(%-2f0FIz!^J%^ z{%q(w6anSIr_!rU<2C`QCB7L_5jDg?zKq_mLK>7nH zV&p+dq<4}H@)Ky<8toc^AHG^ z^dhelZ@%s=OyV*oYHFE%)&1}Qt|T)jNtF6&-7n*vN&euo|700erjrIps1C|PAZZ_1*3sl*#{%+$<9%zoZ^47f~50n~nZ3Vfyg`M}6XKCd0jiTMd3BM})+9`rFeRJOE zGnVkYjh5Bux+?s>uk9Cp%CQWxkM(!GM$hICGMEow=jtvyhmbydGYu5-qM=9GFX#CT z1=aGgCiSk_{1KqZlchwR76a6l*KT_FeQAH3Dm~o$6nOJk_-0%t@u3F(SVoZRyVWMS zmt)owpA+KS_bB2QDks~kSG)A+_;(j{Ds7SMAYtja%6v!tEI_43frg`-SgZvrC+is| zChhYau;q4bi|KieztiE4s@zcqqtZgpu+-?85hY%&CVVYt+H&5~=mcf+4ky7YNw7M~ z@qPAiOPiYBPk%g((9w9SDajEiUHWuQ`wHtoe^Y$U!yk9)8Lk47i%nZ>Rc=#xkAH7* zE_qPp+h2R${;l&Db07z7&_U_A((Z7hLVdXt#tOD$u~c`-~74U6)dgxA^KM0gc_uj2>WHb+4j z7n*c;5qsRdRQ`hz@N2nR-P()!U%A&Hk;>g6SF1t{OQQ5yqWVN%79-9$e#_Qb&hOt2 zr;IAHnp38QMB>~5zFmhF*;W+@75>;B(dU{UrM3JRI?ub@IIwk!FPho8Swnr;g1E}JRY?|5_rJ^QxY*nE}wnmfJOXHNYuny$5J{YU=l*a-xa z#-b)yp?K>n$hdYuox98QGt&lOSInwpSy<>Zd+&*n}a|&L#Ji-ig4MX=#^A=f1d~W zKthX17S$Ai;jH*Rl!H#bYN7PiXauXoS5!@e9p|G_8{xt|R4uqQ>KBQo_%wTi)>)S2 zy*XP+?5;I;ZcFC~uNmRoCua);Gwf0iAb~pgNkJc;+=N7SDF6U*wqZfHy0T^1_Qj`y zEgG+#Gitr?y_8|sJX{KUrSX0{pHO&#bMAUN49n#ov2ln*`g7wWYO0ghPhRF$9#a*+ z{^ul(7iR+-*ZlF?aqK-wa!Q@y*DZXy-fZ>u-&*E6;06b+ev=mcr)E0ZLI*fTrtU@O zo(d9#QxvZ~cx1rleV)+V8e+Grev|-^+5DGB$(A(hjN+ z=JrEJyw9xFaCB0sT?Vk^lxN82>&%lIkC_W>Jo0cI_pcx0q5IG(tk226_E??1I)ijp zN=@|mq+WCC7Uv4TRXP8(%&y=1qQX=&t*I|2nOmp@5M2K9f0KL81d6I*Wd@BDUxU0) z3&&<;Tp>L51oYa6217JVD5=1N%s}5>>vuFZ#t^BmA8%imb0)|S*g&i?8^H3gL^Y*B z(XVW?9It2&Nw0N9&qVivY<>#Bg5TcZz5o>$)n;6<_B4hMF9L?i>>0DUtzD`kjiGlk zk8hv*m@@#~)6cr1>4qMs4y$s-h@Fi6b#Ocm@}a?T&gY8bcMkK>pX|3xd6cgBaUD^T z*feJRa(;om+5h!jtg)<4aAiTqTiCK}1s)(;VBm!Zz6Nw`sGN6X<-{KzrI$^XQwiKA zA~@F})Gt8)>Sq_%_ndLxoq$~6OLLz&GG^j&87;kze1j~)yLijc$s$i@LN5d{{?UW- z6;OA<>%XDPt^sw>g&_|1jG9kO!=+U9EyvpkaAfEZwwi<>ZXskPyHD|G14Q!c0)fqx z+UpEsCSzM=mA`IWh7aZixGMqIoUL+Sc1@Y`AbD2@chU+ewL8_#kHM6?Gkg@ridZ8q zuQlrAMMW83oS%M!i8$HFueaf%$H1J9=C0z*E;{YwggHLN593DV1Ks7yo3a$}O7EFQ z6)j)ilWP26U(NC65v7huYD0F0vJyrn3U9fQQ(JwLn*J_bAQG*MYk)mw3zMghHN1|& zHp^0i@KltFHdo%HY*^*SZDxXF0~Offq$p$iNNxdc{&;Lz3dJH32VT_5H|e0R2{PLgkG9=oRh%@&1>0NeDnY81lL1@fIpIQ+k!;CZ)c3q_c`+>8 z7(3y0D$1F~R{ozM=Mns8_!p(s?_M=(T3z|FvVCJl?P$IzitrM?fh z_b3mpr|4Ky>U0S|z11WO>2wRZwm1d4G;*_AX|5M1UEn}s2Ph!MiZ{@X}339fl>LA@1HrDSqM z5%%JFMRP{hejUSaAtIl{XB@$uGp9RT{%^wG%IeMi@RSg)fBxyZ=t<@9qzNqd@@Umk z!K(;i3;a&{x8yCEd>ByOS5shtDoQ>tgX+ELQ*!7%58DxR7f&X*P-S)@)Jm9v!&X+<-oom|~XgX=k2k(g1QuvvQzzmk@-fKXe>O5DNr?k(g z)oar~w!Hd({~0H;d{VL{AXv4r^3Dh5d@Wjlgyc(viGsXShQr|`8T@EU!&y*eO#P2- zQib{a=75hBoc-Dq~%IOY-}$tT5~tWzTT zu3{q5u&Q||YoS@sgyz>!65PY64g0Jv6~j_07EMK>LNtO-$FA;2A6-2@wdH3ll2;V1 zC0OnXz_sPF-~RUem+m7!8wzLoAb2`@6*c(7r!l1);i5-Ga}=Gj&OS>pjgCxoF>4WQhbAS-q1PVb0F?6k1z18 z(7kP6}z_o*$&JfoiaN5{4gtD2)vj^p>ncoQ;Z~ttOd|O~_8G zi_C>I8Dp~!;O09RLN{XMeLMo>j)&Y<@y3 zhW$HR`P#VTyOi(ZA_F?52?p^A)eU|lSS;|`Rlm0M<=co?rWcMrG8M%dzkQ9B3!WFT zGfxTQ-=~HS?+^DJ^)msnl-I2g`MLQHwWYeVeSmvo0-Sagm?KA`B~+ zGg?$l_Xn6ix5Q-r@Z6(0Y@4?mN)hleeRyudS)^YUx170@{G;t?Sm%n{bf=!K+D-VG z*m1h7Rroy956WlT4|S}1Emw-<^WW0F_^e#LPoK(z zoV%BK{ziiF=gjki7Fq510&6|1N!6}Z3 zKK|3yN0edx?9@t9sjkHr;rOqUAdVy#^M%;Uz#W;=$DVu?aC+>FDveH6DBp<+lxy&W z3&TxxQhW+-uTf6NWR{mxneW>ww=}eF1aH~Rv(vFmqi6bItlVmGNqoHdt|R4jNZ_4= ztXsN&M)^9-Xk@EJU0^La<*Tqm_PQ%wShES!^j{)LhOQ|ar#CCQ4hZqPDS9chC0Edi z-LYFbpIt|Up?i;A01VY91O3NDxs*3JiC{)p+&+}a%z+AN@Ct+auy|>p(wBKigAsKmP~{1fIsjLay22a9X98D8qoma1pVM z=62PsoO`8BfFeu^vc-ET$n^eCz&je)8oS~CY3KEgT6*Dyo4)y>m#~*`!CK&n)FZk@ zX~;W?U5Y${muR;*2DEl%q>pW)x)%Uwwm*e@HYkdj=8g-s^B!!aVT;DvQ7u6jS0tpF zSx2S>ET!`e_FV9&zEQYS5?`);;=J?9osi=}ms4|(#IuFZOzD~F%S|Yg?w;bUdz=)~ z*_$fMG^)$nZcY1!^c$?w!cUkFeQ(tnZ%ry)MC_;g&hH}I6eS)G7fxZt#UUkK_d`}{ zZSxeFb4qaTd??743<@$F&d)bXQ0hHmdECF2KSwp8z1+ukIUOA}BOC+U`9gXiJG8yx zt3X{v9n$$sMu%D#%w7`n{Z}gQHrOv00cINDKwUU!bX`x0y$+f2fa(?;*HJJGDWdj< z%kWIo3PF$c|kg{!)tAeg0IgbIA@vWOVrm zFs;bxVjgN>TQW58pu40`Nh!bz?3dkpY`^r0o1b98-jzJ+eio=1kP3I8%R@RA{aEF$ zqrP9YPo~qVhnPX@ptc1lAUw7Bs+V8)J+Irn=Tgx#40iqiadQDT&`8^D4zsTQ#RQb5n1`cp>%uar-?q; z8C-$bBV?^wZWX!Ed@pxADXSw;>h@hmTWZePcXe(hcX1W^+sB%vMDbU%VxH91nCvC7 zj$})Gxb8=uYiULFdC02cMZsUO#lR(=gdpD|v+*<40My`th#Lo8F%;hfop)hocM%nY;>}sCs=SUZvaH$tXwOG5A6mx8fb;UCd)`s^1p)Fo2)kCtSGy z{hjDCGjLSnMsS4{>rh=EZ)5j8lhlC@Xy`K z|LShZUG%1Q;36%xRV}5ca^)|Fq$9l-L)=P&L1O}c^Djxn@^tQX4H{=#>d{Ir97Q0@ zN-gkcfpsEtot=ou?2fN;53l`NMxRpRG|6KteTYX}Td6@GFX~Hpe2pg7*g+hFQ6*KK|toE!~sM5Z%ifUjMv*Hb@8tCIb?Q6zpVzxcpmqtuzf_$nO(-fF7 zu!uGiGPLfkJ2P)VFnU_MjZ&al-OFHhV9o1exmb{Yr7bYuhjU*S&Oh{Xn$~v8dBh@q z;OEo?ukY*8gs#eEerrRuP#N-aGE>DGGH2Qwkhm#9|Yd_6xS(q6#-Kf2Y;>jHC_u&iK#qhnpmwR+liQ{nabyH@0b6gwwkbJ z>2(M}2^N?WjOv66%x?lDPhlc?2+s%U58-TRRDez5Qpc8IqRffHUhc$NGc{?FihJfB z^O5Jd{l1{Ve{M9IRyu>54fl ztTujY zr-TZTtdEs6a?m#-v^{ve+45-k5@Pb%UY}`}?dANP&R)BhOZ9tX;Sep+3AdPf3NGTF_U(>jU@k* z7`C7A?ET?Ww}cF}hm`Woz)WXFH*Kufe#lsAXiBQI2?O*b#s2f@{+Rm!={8A1`WfBvKs-TfCK{)`l2k02jCrh0tu(Sl3w--On}UBHD6ECF0tnzw+AGBiuaVt{vj z=0epfS8yYu->NBmNJcT8O?onEw&a`p?gq0K^k41WnU)EJ(5N&%EZl%<%yLB^E(!Bu z<@5W?8TqxzS(?y7p!nVw6QQPuYehqP;G~v26OpeHY!@Pb;LzJ|A>b4`7ceryXa}pVYEoirT7xr7iG{i!Wf7yQs$2bSYj;`i%M}!Dft2$ zGi(CB?yco<{$5rYw)Gw=olEz~ukHQZ?yylbxaQEs|AZMqGu!{S{Eor8#p&O8lK&P1 zP91GswJk*AnA3mJ_0<_>4~$UEO$bI3o7pMk2kU7za2C{4H)>gX?pQh`I-TU_2HeGh zVFDa#`8&WTc(?uD;#rZC#v@$tLGvT{;GHM+*U~-HeByMS8LW~&!G@c#oQ4L>JQ=x- z(|Zk|?b!XPv2XVz$vZU*10Smfy{!=q-Lf0H?{Yg&KMTn0cI|au^>HSm)gn$gGoCIr zRB^79?G@5*Nb-)@PD;3j9A_LOJgO7vNC>St&d{UWaQ^pe5`1mI(Fy^ap6^Oz!lKP; zJEq#OJU2yT9eLlH|6Xe=xin$f(6VDLtE+Y&C%|IVJ-``isU%jTEcj0N;t;%ZBymFl zU^DqXF6GmwnHYEhA5%6vW#*C2YRHzKh#%qtB}8Qy8^@VxIeo7C|zK8W8g4-OB{*`dl^>p3d* zpY&QZfbIdOt^@QJOZ_KCK-aFZU6!IK*(pKBy@84|%a7^|@6RN#+hN`GVWE(_`?nc& zR7#}Z)PKB@fBh$I)fiU zXmLm~Mvu2~eBM&22Rw#uV`p~>SjquVO7dajBIl2^3`zx3GqO?7=D)6Ep*cVIGibw8 zxQ6;a{+_8u5OZCo|JFT=p|dT^iTA+;O9B4K65iY=vBbH?B!{>8;zQH918Q_>u$H$M zgXBFe6HAk9^LOkzmX)ipLF1b4$zjfIs{n@12|)n%1HcNk0rV7(TI8cchOBM(_Trf= zvZ=dQuns}TEi`>J;9*C4cFEzRODF8pW4D}Vwv3}S$Pa6`aAb*jF#qK%JP<{oaS=wx1JLY3agA+NNt~_=53lh?H*B3@<5t&(Pua~P zO$Ry8{)1*ajact2@g`afh*lp)gD(>k$|E3YA7f0#`p}|5c99c}=0(DWdwMem8@xvC%s94<^(c{Z zecDmJ+cy?6{xBZjT#U}t`q%Zi#Hs0r=s!X~)L!+|cSJgZ#NX^0P(!+DeMWTNO(QOp zCOhB$m^ZAyw&a*JwprB+Y<3UA+kC7Jyk63#-upU&^So%s35`^*N&FTdLhIM&BwOoh zQXr-VjkwwL@`fR(l}zc?q8_jSrfOh$8|eeDtEMoJq^GJp<6X7=qmIvQhbtn8`g^cf zU%NY&J2LSfy5t=v_^8* zjucVn6K;060 zyWKx{k4|h5Sg0tCw{rPJ`xwN%*lUg3lxzQLxlikH@>%B#=p;i;j{`ZZ8If=f6B|?s zTP8xcIB@y|BmL)G1)Out^RY$P2ZTU?MsM zB5KM}&uwUup9HK7m#3w^qMG-${?RX6Rcq{o=;p&@2^efMBs5z zlVnM2M2XNu!DM!9ys+XVIEb8XVtKB0jtgK3%MkU_2=iNfp~n@fZhp=3{+1kll%3j zv&{(oK79z%wn!|lepg-bit)eqVoc<2mo9zvNvuHXLz8CHMsib%m$pKpHv|^@&t2kR z9xY4a@YIecptFvj-49ljUwF_>G*1g?x-(E(xqhBwyhjcWJFCUqRq;0!7wq2lGk0G1 zmxsQQLi&FWNGmvr-#WGxNO~x@{J0&{3-W>*{vEJ#cYCi#4ZAvFRsQJ`Q_+6xexw3k zgNM6?v;#f+C~(nMVa)dWlH1s4h!Ms~Q-5uT^b4cabu|)V@&O4VPKO{Q7p<0nwrK%i zI1ciQN`Ss{s+hJhe|ER&Zdw4RXgi?0<8C27PR`y3Jtj@>#EKVZv~h@jUh4&|Cr2+V zb?e6tWS!;`anV+tmK)eEDeM(Rf1K5x)exvq6EAJx8ZNuzp}!kC`fC~N|2aXK$j_-F ztOK!8-;>Mgl`CAlDneiV5O*FFy78tJyV6s&D(_nmvOC5@uBUslX&@u8ouj@0ejt*9 zX6SSjX1%W<*UF2EC<&|&QSN-Kty{H!O!W2#chS3a5i7YwX59U^7o8z04$o_#ULsF= z#2rEvMVa86{kcu_>)-hBnLGW1tjwE^XgoFaJoY>nnp&L7yBo?A$G!!zUetx`-m+}0 ze!NRU3#w^ysi((@q3X~XmW|5%8_h&IJryrMiwPj}E-$>#3h6Txs4fBzgzLt(c+|Q& zQ#~i>xPU{?4*%t|aWen1l%01AE_^3q2_ff(0lu9L5^1YWxv3Gb-(33K^xmD5 z4=@Q(j1#_G1&!mJ#hySl2_7v;qgzd`31f}znX?ETSQf5t9SOf+bEDTiWfm)toH)u` zudNqIn6Ygo0mOdR;-?zBE7?S#LUr(HjQHnpE=-$yCt&z-6lD*$9Gh}>%|5Gi7?`_#ZPKhNIF)t;2AoXB? z@s$+fgv@}ko#62tpj`kT!#Qq|<;F(?B7csrG%!?R0_1SI6?j9+8%ZZ*kGjhDi-&^X zLwBk(c&@tHa&4c)EcIs`L^6+5?>)8hXn#5HqzAG7_OYA|@oVRllX%nuKAfaB_QInO;J9nfggxS0#)b==te zy~J{bm{-YLlGqBWN4mS~i>wy5oxSBVfLR58 nnt}Q0|EFjA>$07eOP4sFk-F-rUNsp^BW7iJ4Y?v2Q^fxQ`H-}| diff --git a/public/img/dev-tip/wrongtitle-dark.png b/public/img/dev-tip/wrongtitle-dark.png deleted file mode 100644 index ab325168386be355de79314d7013b9ccf17f6373..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8914 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 diff --git a/public/img/dev-tip/wrongtitle-light.png b/public/img/dev-tip/wrongtitle-light.png deleted file mode 100644 index ef23850c735f86d549e8182c1964fd931644b00d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9010 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 SHv) diff --git a/public/img/dev-tip/wrongtranslateendline-dark.png b/public/img/dev-tip/wrongtranslateendline-dark.png deleted file mode 100644 index d9a50de4878e91c57e471913a0db1560ec31cba1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166801 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%0DE}V|!O-LX3Zw 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�yhZ91d=Lb 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∋#UhP6Yi`jRto8 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 diff --git a/public/img/dev-tip/wrongtranslateendline-light.png b/public/img/dev-tip/wrongtranslateendline-light.png deleted file mode 100644 index 3c7c0c508344d19c25140308cba8fc904ee266d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168209 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
QEy!Wf7v(dzP=nu#v^vu>;Ne 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�)Qr$`%-$Biol9wjxAE6{svEUY9bZeH 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 diff --git a/public/logo.svg b/public/logo.svg deleted file mode 100644 index 54a87a1dd..000000000 --- a/public/logo.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/public/logo.webp b/public/logo.webp deleted file mode 100644 index c70c25732e6f44b46e670409b592f6d62da08a1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61596 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

e#1fB~9Ac|N&G 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 S0000000000000000002znE00f diff --git a/snippets/snippet-with-region.js b/snippets/snippet-with-region.js deleted file mode 100644 index 9c7faaebc..000000000 --- a/snippets/snippet-with-region.js +++ /dev/null @@ -1,7 +0,0 @@ -// #region snippet -function foo() { - // .. -} -// #endregion snippet - -export default foo diff --git a/snippets/snippet.js b/snippets/snippet.js deleted file mode 100644 index 575039d1e..000000000 --- a/snippets/snippet.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function () { - // .. -} diff --git a/tsconfig.json b/tsconfig.json index 744de2730..36ca26037 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,20 @@ { "compilerOptions": { "target": "ESNext", - "jsx": "preserve", - "lib": ["DOM", "ESNext"], - "baseUrl": ".", "module": "ESNext", - "moduleResolution": "node", - "resolveJsonModule": true, + "moduleResolution": "Bundler", + "lib": ["DOM", "ESNext"], + "jsx": "preserve", "strict": true, - "strictNullChecks": true, "noUnusedLocals": true, + "resolveJsonModule": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, - "allowSyntheticDefaultImports": true, "allowImportingTsExtensions": true, "noEmit": true, - "typeRoots": ["./node_modules/@types"] + "types": ["node", "vitepress/client"] }, - "exclude": ["dist", "node_modules"] + "include": [".vitepress/**/*.ts", ".vitepress/**/*.mts", ".vitepress/**/*.vue"], + "exclude": ["dist", "node_modules", ".vitepress/cache"] } diff --git a/types/image.d.ts b/types/image.d.ts deleted file mode 100644 index 377d4ff08..000000000 --- a/types/image.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module '*.webp' -declare module '*.png' -declare module '*.svg' diff --git a/utils/ensure-full-git.js b/utils/ensure-full-git.js index 1521822df..a9d632a87 100644 --- a/utils/ensure-full-git.js +++ b/utils/ensure-full-git.js @@ -31,7 +31,7 @@ async function checkAndRestoreHistory() { console.error('❌ Failed to restore Git history:', error.message) // process.exit(1) console.warn( - '! This may result in incomplete contributor information in `valaxy-addon-git-log`. Consider manually running: git fetch --unshallow' + '! This may result in incomplete page contributors and last-updated times. Consider manually running: git fetch --unshallow' ) } } diff --git a/utils/languageDetector.ts b/utils/languageDetector.ts deleted file mode 100644 index 61bb06aa6..000000000 --- a/utils/languageDetector.ts +++ /dev/null @@ -1,136 +0,0 @@ -const SUPPORTED_LANGUAGES = ['en', 'zh-CN'] as const -const DEFAULT_LANGUAGE = 'en' - -function detectBrowserLanguage(): string { - const browserLanguages = navigator.languages || [navigator.language] - - if (import.meta.env.DEV) { - console.log('Browser languages:', browserLanguages) - console.log('Supported languages:', SUPPORTED_LANGUAGES) - } - - for (const browserLang of browserLanguages) { - if (SUPPORTED_LANGUAGES.includes(browserLang as any)) { - if (import.meta.env.DEV) { - console.log('Exact match found:', browserLang) - } - return browserLang - } - } - - for (const browserLang of browserLanguages) { - const langCode = browserLang.split('-')[0] - const matchedLang = SUPPORTED_LANGUAGES.find(lang => lang.startsWith(langCode)) - if (matchedLang) { - if (import.meta.env.DEV) { - console.log('Language code match found:', matchedLang, 'for browser lang:', browserLang) - } - return matchedLang - } - } - - if (import.meta.env.DEV) { - console.log('⚠️ No match found, using default language:', DEFAULT_LANGUAGE) - } - return DEFAULT_LANGUAGE -} - -function setLanguage(lang: string, locale?: any) { - if (locale && locale.value !== lang) { - if (import.meta.env.DEV) { - console.log('Setting language from', locale.value, 'to', lang) - } - - locale.value = lang - } - - if (typeof document !== 'undefined') { - document.documentElement.lang = lang - } - - if (typeof localStorage !== 'undefined') { - localStorage.setItem('valaxy-lang', lang) - } - - if (import.meta.env.DEV) { - console.log(`Language switched to: ${lang}`) - } -} - -export function initLanguageDetection(locale?: any) { - try { - let sessionInitialized: string | null = null - if (typeof sessionStorage !== 'undefined') { - sessionInitialized = sessionStorage.getItem('valaxy-lang-session-initialized') - } - - if (sessionInitialized === 'true') { - if (import.meta.env.DEV) { - console.log('Language already initialized in this session, skipping') - } - return - } - - let savedLang: string | null = null - let hasVisitedBefore: string | null = null - - if (typeof localStorage !== 'undefined') { - savedLang = localStorage.getItem('valaxy-lang') - hasVisitedBefore = localStorage.getItem('valaxy-lang-initialized') - } - - if (import.meta.env.DEV) { - console.log('Saved language preference:', savedLang) - console.log('Has visited before:', hasVisitedBefore) - } - - if (hasVisitedBefore === 'true') { - if (savedLang && SUPPORTED_LANGUAGES.includes(savedLang as any)) { - if (import.meta.env.DEV) { - console.log('Using saved language preference (returning user):', savedLang) - } - setLanguage(savedLang, locale) - } else { - const currentLang = locale?.value || DEFAULT_LANGUAGE - if (import.meta.env.DEV) { - console.log('Using current/default language (returning user):', currentLang) - } - setLanguage(currentLang, locale) - } - } else { - const detectedLang = detectBrowserLanguage() - if (import.meta.env.DEV) { - console.log('First visit - auto-detected language:', detectedLang) - } - - setLanguage(detectedLang, locale) - - if (typeof localStorage !== 'undefined') { - localStorage.setItem('valaxy-lang-initialized', 'true') - } - } - - if (typeof sessionStorage !== 'undefined') { - sessionStorage.setItem('valaxy-lang-session-initialized', 'true') - } - } catch (error) { - console.error('ERROR in language initialization:', error) - setLanguage(DEFAULT_LANGUAGE, locale) - if (typeof sessionStorage !== 'undefined') { - sessionStorage.setItem('valaxy-lang-session-initialized', 'true') - } - } -} - -export function setLanguageManually(lang: string, locale?: any) { - if (SUPPORTED_LANGUAGES.includes(lang as any)) { - setLanguage(lang, locale) - - if (typeof localStorage !== 'undefined') { - localStorage.setItem('valaxy-lang-initialized', 'true') - } - if (typeof sessionStorage !== 'undefined') { - sessionStorage.setItem('valaxy-lang-session-initialized', 'true') - } - } -} diff --git a/valaxy.config.ts b/valaxy.config.ts deleted file mode 100644 index cd0690ea5..000000000 --- a/valaxy.config.ts +++ /dev/null @@ -1,201 +0,0 @@ -import type { PressTheme } from 'valaxy-theme-press' -import process from 'node:process' -import { $t, defineValaxyConfig } from 'valaxy' -import { addonAlgolia } from 'valaxy-addon-algolia' -import { addonComponents } from 'valaxy-addon-components' -import { addonGitLog } from 'valaxy-addon-git-log' - -import { localIconLoader } from 'vitepress-plugin-group-icons' -import { markdownThemeImagePlugin } from './utils/markdown-theme-image' -import { fetchOpenListVersion } from './utils/version' - -const COMMIT_TAG = process.env.COMMIT_TAG || (await fetchOpenListVersion()) || 'dev' -const DOCS_COMMIT_SHA = process.env.CF_PAGES_COMMIT_SHA || process.env.DOCS_COMMIT_SHA -const DOCS_ICP = process.env.DOCS_ICP || '' -const DOCS_CN = DOCS_ICP !== '' -const DOCS_BUILT_DATE = new Date().toLocaleString('zh-CN', { hour12: false }).replace('T', ' ') -// Must have SITE_URL or build fails -const SITE_URL = process.env.SITE_URL || 'https://doc.oplist.org' -const VITE_BASE = process.env.VITE_BASE || '/' - -const safelist = ['i-ri-home-line', 'i-ri-github-line', 'i-ri-arrow-up-line'] - -export default defineValaxyConfig({ - siteConfig: { - title: $t('siteConfig.title'), - url: SITE_URL, - description: $t('siteConfig.description'), - - author: { - name: 'OpenList Team', - link: 'https://github.com/OpenListTeam', - avatar: `${SITE_URL}/logo.svg`, - }, - - comment: { - enable: DOCS_CN ? false : true, - }, - - search: { - enable: true, - type: 'algolia', - }, - - encrypt: { - enable: false, - }, - - mediumZoom: { - enable: true, - }, - - statistics: { - enable: true, - }, - - license: { - enabled: false, - }, - }, - - addons: [ - // Cannot uncomment this, or build error - addonAlgolia({ - appId: 'I80ZVXR7H9', - apiKey: '2a3fe6e1f95a3c8ca60fee55e44c00a1', - indexName: 'Document Crawler CN', - }), - addonComponents(), - addonGitLog({ - contributor: { - strategy: 'prebuilt', - }, - repositoryUrl: 'https://github.com/OpenListTeam/OpenList-Docs', - }), - ], - - theme: 'press', - themeConfig: { - logo: `${VITE_BASE.replace(/\/$/, '')}/logo.svg`, - sidebar: ['guide', 'configuration', 'faq', 'seeds', 'api', 'migration', 'ecosystem'], - socialLinks: [{ icon: 'i-ri-github-line', link: 'https://github.com/OpenListTeam/OpenList' }], - nav: [ - { - text: 'nav.guide', - link: '/guide', - }, - { - text: 'nav.configuration', - link: '/configuration', - }, - { - text: 'nav.faq', - link: '/faq', - }, - { - text: 'nav.seeds', - link: '/seeds', - }, - { - text: 'nav.ecosystem', - link: '/ecosystem', - }, - { - text: 'nav.community.title', - items: [ - { - text: 'nav.community.discussions', - link: 'https://github.com/OpenListTeam/OpenList/discussions', - }, - { - text: 'nav.community.telegram', - link: 'https://t.me/OpenListTeam', - }, - { - text: 'nav.community.telegram_channel', - link: 'https://t.me/OpenListOfficial', - }, - ], - }, - { - text: COMMIT_TAG, - items: [ - { - text: 'Release Notes', - link: 'https://github.com/OpenListTeam/OpenList/releases', - }, - ], - }, - ], - - // Why default to be `https://github.com/YunYouJun/valaxy/edit/main/docs/:path`? - editLink: { - pattern: 'https://github.com/OpenListTeam/OpenList-Docs/edit/main/:path', - }, - - footer: { - message: DOCS_COMMIT_SHA - ? `Commit ${DOCS_COMMIT_SHA?.slice(0, 8)} built at ${DOCS_BUILT_DATE}` - : `Built at ${DOCS_BUILT_DATE}`, - copyright: `AGPL-3.0 Licensed | © 2022-${new Date().getFullYear()} The OpenList Projects Contributors${DOCS_ICP ? ' | ' + DOCS_ICP + '' : ''}`, - }, - }, - - vite: { - base: VITE_BASE, - resolve: { - alias: { - 'valaxy-theme-press/components/PressNavScreenMenuLink.vue': - './components/PressNavScreenMenuLink.vue', - }, - }, - }, - unocss: { - safelist, - }, - - markdown: { - blocks: { - tip: { - icon: 'i-carbon-thumbs-up', - }, - warning: { - icon: 'i-carbon-warning-alt', - }, - danger: { - icon: 'i-carbon-warning', - }, - info: { - icon: 'i-carbon-information', - }, - }, - - codeTransformers: [ - // We use `[!!code` in demo to prevent transformation, here we revert it back. - { - postprocess(code) { - return code.replace(/\[!!code/g, '[!code') - }, - }, - ], - // theme: 'material-theme-palenight', - config: md => { - md.use(markdownThemeImagePlugin) - }, - }, - - groupIcons: { - customIcon: { - valaxy: localIconLoader(import.meta.url, './public/favicon.svg'), - docker: 'vscode-icons:file-type-docker', - nginx: 'vscode-icons:file-type-nginx', - toml: 'vscode-icons:file-type-toml', - }, - }, - - modules: { - rss: { - enable: false, - }, - }, -}) From 81573f11518eb9409101e927ae6eaf50417d9e00 Mon Sep 17 00:00:00 2001 From: AdingApkgg Date: Sun, 27 Sep 2026 09:29:04 +0800 Subject: [PATCH 02/13] chore(lint): use Biome for code formatting and linting Biome 2.5 formats and lints the site code (TS, Vue script blocks, CSS, JSON) with the same style as the previous Prettier config. Prettier is kept for Markdown and YAML, which Biome does not support. - `bun run format` / `format:check` run Biome, then Prettier on *.md / *.yml; lint-staged does the same per file type. - Vue templates are not visible to Biome's stable Vue support, so the unused-variable/import rules are off for *.vue. - Fix the lint findings: `any` types, non-null assertions, `==`, global `isNaN`, assignment in return, `node:` import protocol. Co-Authored-By: Claude Opus 5.5 --- .github/PULL_REQUEST_TEMPLATE.md | 4 +- .vitepress/config.mts | 2 +- .vitepress/plugins/mermaid.ts | 4 +- .vitepress/plugins/theme-image.ts | 10 ++-- .vitepress/sidebar.ts | 2 +- .vitepress/theme/components/BackToTop.vue | 2 +- .vitepress/theme/components/BiliBili.vue | 9 +-- .../theme/components/OpenListDownload.vue | 4 +- .../theme/components/WorkInProgress.vue | 1 + .vitepress/theme/i18n.ts | 8 ++- .vitepress/theme/index.ts | 6 +- .vitepress/version.ts | 18 ++++-- biome.json | 60 +++++++++++++++++++ bun.lock | 19 ++++++ package.json | 10 +++- utils/ensure-full-git.js | 4 +- 16 files changed, 129 insertions(+), 34 deletions(-) create mode 100644 biome.json diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index bcdfd12a2..ba8637ea6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -39,8 +39,8 @@ Relates to #XXXX - [ ] I have read the [CONTRIBUTING](https://github.com/OpenListTeam/OpenList-Docs/blob/main/CONTRIBUTING.md) document. 我已阅读 [CONTRIBUTING](https://github.com/OpenListTeam/OpenList-Docs/blob/main/CONTRIBUTING.md) 文档。 -- [ ] I have formatted my code or documentation with [prettier](https://prettier.io/) or other appropriate formatter. - 我已使用 [prettier](https://prettier.io/) 或其他适当的格式化工具格式化提交的代码或文档。 +- [ ] I have formatted my code or documentation with `bun run format` ([Biome](https://biomejs.dev/) for code, [Prettier](https://prettier.io/) for Markdown). + 我已使用 `bun run format` 格式化提交的代码或文档(代码使用 [Biome](https://biomejs.dev/),Markdown 使用 [Prettier](https://prettier.io/))。 - [ ] I have updated all supported languages (including Chinese and English) for documentation. (If it's needed) 我已为所有支持语言(包括中文和英文)更新文档内容。 (若适用) - [ ] I have verified that the written documentation or code is properly formatted, with no syntax errors, spelling mistakes. diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 3f95ca9af..632962c26 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -1,6 +1,6 @@ import process from 'node:process' import { fileURLToPath } from 'node:url' -import { defineConfigWithTheme, type DefaultTheme } from 'vitepress' +import { type DefaultTheme, defineConfigWithTheme } from 'vitepress' import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons' import { getContributors } from './contributors' import { mermaidPlugin } from './plugins/mermaid' diff --git a/.vitepress/plugins/mermaid.ts b/.vitepress/plugins/mermaid.ts index 170ce007f..07c67c591 100644 --- a/.vitepress/plugins/mermaid.ts +++ b/.vitepress/plugins/mermaid.ts @@ -4,7 +4,9 @@ import type { MarkdownRenderer } from 'vitepress' * Render ```mermaid fences with the client-side component. */ export function mermaidPlugin(md: MarkdownRenderer) { - const fence = md.renderer.rules.fence! + const fence: NonNullable = + md.renderer.rules.fence ?? + ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options)) md.renderer.rules.fence = (tokens, idx, options, env, self) => { const token = tokens[idx] diff --git a/.vitepress/plugins/theme-image.ts b/.vitepress/plugins/theme-image.ts index 0124a3fa6..bf7745a81 100644 --- a/.vitepress/plugins/theme-image.ts +++ b/.vitepress/plugins/theme-image.ts @@ -4,13 +4,11 @@ import type { MarkdownRenderer } from 'vitepress' * Render `![](a.png#light)` + `![](a.png#dark)` pairs as a light/dark switching image. */ export function themeImagePlugin(md: MarkdownRenderer) { - const defaultImageRender = - md.renderer.rules.image || - function (tokens: any, idx: any, options: any, _env: any, renderer: any) { - return renderer.renderToken(tokens, idx, options) - } + const defaultImageRender: NonNullable = + md.renderer.rules.image ?? + ((tokens, idx, options, _env, renderer) => renderer.renderToken(tokens, idx, options)) - md.renderer.rules.image = function (tokens, idx, options, env, renderer) { + md.renderer.rules.image = (tokens, idx, options, env, renderer) => { const token = tokens[idx] const src = token.attrGet('src') || '' const alt = token.content || '' diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 49e95a915..c50703703 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -35,7 +35,7 @@ function walk(dir: string): string[] { } function toLink(relativePath: string): string { - return '/' + relativePath.replace(/(^|\/)index\.md$/, '$1').replace(/\.md$/, '') + return `/${relativePath.replace(/(^|\/)index\.md$/, '$1').replace(/\.md$/, '')}` } function readPages(srcDir: string, localePrefix: string, otherLocales: string[]): PageEntry[] { diff --git a/.vitepress/theme/components/BackToTop.vue b/.vitepress/theme/components/BackToTop.vue index cbc313ed8..3bc5aeb0c 100644 --- a/.vitepress/theme/components/BackToTop.vue +++ b/.vitepress/theme/components/BackToTop.vue @@ -18,7 +18,7 @@ diff --git a/.vitepress/theme/i18n.ts b/.vitepress/theme/i18n.ts index 0635a0472..839cbc190 100644 --- a/.vitepress/theme/i18n.ts +++ b/.vitepress/theme/i18n.ts @@ -1,5 +1,5 @@ -import { computed } from 'vue' import { useData } from 'vitepress' +import { computed } from 'vue' const messages = { en: { @@ -52,8 +52,10 @@ export function useI18n() { const locale = computed(() => (lang.value in messages ? (lang.value as Locale) : 'en')) function t(key: string): string { - let value: any = messages[locale.value] - for (const part of key.split('.')) value = value?.[part] + let value: unknown = messages[locale.value] + for (const part of key.split('.')) + value = + value && typeof value === 'object' ? (value as Record)[part] : undefined return typeof value === 'string' ? value : key } diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index 6ebeff58e..66e3b2331 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -1,15 +1,15 @@ +import mediumZoom from 'medium-zoom' import type { Theme } from 'vitepress' -import DefaultTheme from 'vitepress/theme' import { useRoute } from 'vitepress' -import mediumZoom from 'medium-zoom' +import DefaultTheme from 'vitepress/theme' import { nextTick, onMounted, watch } from 'vue' import 'virtual:group-icons.css' import './style.css' -import Layout from './Layout.vue' import BiliBili from './components/BiliBili.vue' import Mermaid from './components/Mermaid.vue' import OpenListDownload from './components/OpenListDownload.vue' import WorkInProgress from './components/WorkInProgress.vue' +import Layout from './Layout.vue' export default { extends: DefaultTheme, diff --git a/.vitepress/version.ts b/.vitepress/version.ts index 0935746d0..8cf9a9aaf 100644 --- a/.vitepress/version.ts +++ b/.vitepress/version.ts @@ -1,5 +1,12 @@ let openListVersionCache: string | null = null +/** the fields read from the GitHub release / npm package responses */ +interface VersionResponse { + tag_name?: string + version?: string + 'dist-tags'?: { latest?: string } +} + // fetch the OpenList's latest version smartly export const fetchOpenListVersion = async (): Promise => { if (openListVersionCache) return openListVersionCache @@ -8,22 +15,22 @@ export const fetchOpenListVersion = async (): Promise => { // GitHub Releases { url: 'https://api.github.com/repos/OpenListTeam/OpenList/releases/latest', - handler: (json: any) => json.tag_name, + handler: (json: VersionResponse) => json.tag_name, }, // fallback to frontend version number // npm registry { url: 'https://registry.npmjs.org/@openlist-frontend/openlist-frontend/latest', - handler: (json: any) => `v${json.version}`, + handler: (json: VersionResponse) => `v${json.version}`, }, { url: 'https://registry.npmjs.org/@openlist-frontend/openlist-frontend', - handler: (json: any) => `v${json['dist-tags'].latest}`, + handler: (json: VersionResponse) => `v${json['dist-tags']?.latest}`, }, // unpkg { url: 'https://unpkg.com/@openlist-frontend/openlist-frontend/package.json', - handler: (json: any) => `v${json.version}`, + handler: (json: VersionResponse) => `v${json.version}`, }, ] @@ -39,7 +46,8 @@ export const fetchOpenListVersion = async (): Promise => { const version = handler(json) if (typeof version === 'string' && /^v?\d+\.\d+\.\d+$/.test(version)) { - return (openListVersionCache = version.startsWith('v') ? version : `v${version}`) + openListVersionCache = version.startsWith('v') ? version : `v${version}` + return openListVersionCache } } catch (e) { console.warn(`Failed to fetch version from ${url}`, e) diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..9e89e470f --- /dev/null +++ b/biome.json @@ -0,0 +1,60 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": true, + "includes": ["**", "!pages/public"] + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "asNeeded", + "trailingCommas": "es5", + "arrowParentheses": "asNeeded" + } + }, + "html": { + "formatter": { + "indentScriptAndStyle": true + } + }, + "linter": { + "enabled": true, + "rules": { + "preset": "recommended" + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "overrides": [ + { + "includes": ["**/*.vue"], + "linter": { + "rules": { + "correctness": { + "noUnusedVariables": "off", + "noUnusedImports": "off" + }, + "style": { + "useVueMultiWordComponentNames": "off" + } + } + } + } + ] +} diff --git a/bun.lock b/bun.lock index dfd3eebe8..545aef29b 100644 --- a/bun.lock +++ b/bun.lock @@ -14,6 +14,7 @@ "vue": "^3.5.43", }, "devDependencies": { + "@biomejs/biome": "^2.5.14", "@types/node": "^24.19.0", "husky": "^9.1.7", "lint-staged": "^17.6.0", @@ -68,6 +69,24 @@ "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], + "@biomejs/biome": ["@biomejs/biome@2.5.14", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.14", "@biomejs/cli-darwin-x64": "2.5.14", "@biomejs/cli-linux-arm64": "2.5.14", "@biomejs/cli-linux-arm64-musl": "2.5.14", "@biomejs/cli-linux-x64": "2.5.14", "@biomejs/cli-linux-x64-musl": "2.5.14", "@biomejs/cli-win32-arm64": "2.5.14", "@biomejs/cli-win32-x64": "2.5.14" }, "bin": { "biome": "bin/biome" } }, "sha512-0FabLIjd4M/dm8VFI86RMaLLdgepzbdfiAL2R8cr7V81OYYrP1w7Z73KfAwPK5h9SrEBXTzNG2y+mBwPo9xRnw=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UnzaXO65L4tsZimFITFP2M121GyhDcWFrT3pL5ZJ5U4XcS/0L5VHYytVohdCf/gnDUFHgl2I9xnt5bV/J1kxHQ=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-kiy8qA16K93J7uvFfWi4LgjqDpnRKyePAna6A0Y4jxyUga35SYpIZ2cNWlhy0lsfgqRenCpfymnJWEZ6mgpRgA=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-vO/9BaU1n30CiFNLx49gMTMtbCAAqlo/EqFoG0BU3deQInTbJrmXSmTQ9e3FoDZIKQnvSLDVNL4lx1ci7y1T1Q=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-SJ9PrZkBnnH9dHJDnxk34vKs0GB2dbsioaft6/hPhWJ7AHpwYH83Isnhj0FSRpFkGgpVfJ1lds23ApB2czUDLQ=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.14", "", { "os": "linux", "cpu": "x64" }, "sha512-VHZRa7CCQBUWKxNwUvHJeuW03WFRgN5NWO//SDGOitc9QIeNyfA42V9zlKm+x82xFSG9Bzi5fi+hbhm9anO8yA=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.14", "", { "os": "linux", "cpu": "x64" }, "sha512-2kI5PrMgW5dcEZYrstLPmUmCwkUwZY39rP4BN93Vxbwcs2O57LDQOktUZZYPvvspN5i0mhGr3TJtF5sU26NUWg=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-pHgAFffmZtaYoxavEsWEYNvcA7NwOIjLyqw2HXyLbt16xYryX0L4fMV2u0G9ag6LNYPIoqWaWqzUcnc6rLGGXg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.14", "", { "os": "win32", "cpu": "x64" }, "sha512-oJWmBhoHsnhUKIke+0gXDX0mltJrWHA1UyHsrTlXwX0TL64ilVZAo+TYZm97baecV0esBdpzy3k96q+09JaZUQ=="], + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.1.2", "", { "dependencies": { "@chevrotain/gast": "11.1.2", "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q=="], diff --git a/package.json b/package.json index 11ac3b1a0..8d28e663b 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "dev": "vitepress dev", "build": "node utils/ensure-full-git.js && vitepress build", "preview": "vitepress preview", - "format": "prettier --write .", - "format:check": "prettier --check .", + "format": "biome check --write && prettier --write --log-level warn \"**/*.{md,yml,yaml}\"", + "format:check": "biome check && prettier --check --log-level warn \"**/*.{md,yml,yaml}\"", "prepare": "husky" }, "dependencies": { @@ -26,13 +26,17 @@ "vue": "^3.5.43" }, "devDependencies": { + "@biomejs/biome": "^2.5.14", "@types/node": "^24.19.0", "husky": "^9.1.7", "lint-staged": "^17.6.0", "prettier": "^3.9.9" }, "lint-staged": { - "*.{js,ts,mts,vue,md,json,yml,yaml}": [ + "*.{js,ts,mts,vue,json,css}": [ + "biome check --write --no-errors-on-unmatched" + ], + "*.{md,yml,yaml}": [ "prettier --write" ] } diff --git a/utils/ensure-full-git.js b/utils/ensure-full-git.js index a9d632a87..75d67da6c 100644 --- a/utils/ensure-full-git.js +++ b/utils/ensure-full-git.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { execSync } from 'child_process' -import { existsSync } from 'fs' +import { execSync } from 'node:child_process' +import { existsSync } from 'node:fs' /** * Restores complete Git history for shallow clones to fix contributor tracking issues. From 260d54629fd42c97deb2e74592ccffa884ebd7d8 Mon Sep 17 00:00:00 2001 From: AdingApkgg Date: Sun, 27 Sep 2026 09:34:53 +0800 Subject: [PATCH 03/13] chore(typecheck): add vue-tsc typecheck and fix v-if with v-for - Add `bun run typecheck` (vue-tsc + TypeScript 6.0.3). TypeScript 7 is not used yet: it drops the JS compiler API that vue-tsc needs. - OpenListDownload: move the Chinese-only download source row out of the `v-for` element into a `