diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..273fff0a80 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "maven" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/build_installers.yml b/.github/workflows/build_installers.yml index f7beb68b06..b6e6a6eb98 100644 --- a/.github/workflows/build_installers.yml +++ b/.github/workflows/build_installers.yml @@ -24,11 +24,14 @@ jobs: - name: Make installers run: | /opt/install4j/bin/install4jc -L ${{ secrets.INSTALL4J_LICENSE }} -r ${{ steps.java_info.outputs.project_version }} -g -d target/media -D librariesPath=$(pwd)/target/libraries install4j/project.install4j + - name: Delete garbages + run: | + rm -rf target/media/debug* # jarPath=$(pwd) 是必须的,install4jc 不知道什么毛病,不支持相对路径,这太诡异了 - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: installer-dist path: | - target/media/PeerBanHelper_* - id: project \ No newline at end of file + target/media/* + id: project diff --git a/.github/workflows/delete_old_s3_objects.yml b/.github/workflows/delete_old_s3_objects.yml new file mode 100644 index 0000000000..14bf061217 --- /dev/null +++ b/.github/workflows/delete_old_s3_objects.yml @@ -0,0 +1,28 @@ +name: Empty S3 directory + +on: + workflow_call: + secrets: + S3_ACCESS_KEY: + required: true + S3_SECRET_KEY: + required: true + S3_ENDPOINT: + required: true + S3_BUCKET: + required: true +jobs: + delete-files-from-s3: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Remove from S3 + uses: vitorsgomes/s3-rm-action@master + with: + args: --recursive + env: + AWS_S3_BUCKET: ${{ secrets.S3_BUCKET }} + AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }} + AWS_S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }} + PATH_TO_DELETE: updates/latest diff --git a/.github/workflows/flush_cloudflare_cache.yml b/.github/workflows/flush_cloudflare_cache.yml new file mode 100644 index 0000000000..7102abf178 --- /dev/null +++ b/.github/workflows/flush_cloudflare_cache.yml @@ -0,0 +1,20 @@ +name: Flush CloudFlare Update Cache + +on: + workflow_call: + secrets: + FILES_CLOUDFLARE_ZONE: + required: true + FILES_CLOUDFLARE_TOKEN: + required: true +jobs: + purge-cloudflare-cache: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Purge cache + uses: jakejarvis/cloudflare-purge-action@master + env: + # Zone is required by both authentication methods + CLOUDFLARE_ZONE: ${{ secrets.FILES_CLOUDFLARE_ZONE }} + CLOUDFLARE_TOKEN: ${{ secrets.FILES_CLOUDFLARE_TOKEN }} diff --git a/.github/workflows/jvm-ci.yml b/.github/workflows/jvm-ci.yml index 94c09d2bd8..770fc5576f 100644 --- a/.github/workflows/jvm-ci.yml +++ b/.github/workflows/jvm-ci.yml @@ -50,7 +50,7 @@ jobs: password: ${{ secrets.DOCKER_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5.5.1 + uses: docker/metadata-action@v5.6.1 with: images: ghostchu/peerbanhelper-snapshot tags: | @@ -63,7 +63,7 @@ jobs: type=raw,ci type=sha - name: Build and push Docker image - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: context: . file: ./Dockerfile diff --git a/.github/workflows/jvm-release.yml b/.github/workflows/jvm-release.yml index 00336a5640..5475c336a3 100644 --- a/.github/workflows/jvm-release.yml +++ b/.github/workflows/jvm-release.yml @@ -44,7 +44,33 @@ jobs: Build_Portable: needs: Build_Executable uses: ./.github/workflows/build_portable.yml + Empty_Old_S3_Files: + needs: [Build_Executable, Build_Installers, Build_SPK, Build_DEB, Build_PKG, Build_Portable] + uses: ./.github/workflows/delete_old_s3_objects.yml + secrets: inherit + Upload_Updates_To_S3: + needs: [Build_Executable, Build_Installers, Build_SPK, Build_DEB, Build_PKG, Build_Portable, Empty_Old_S3_Files] + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + name: installer-dist + path: target/media + - uses: shallwefootball/s3-upload-action@master + with: + aws_key_id: ${{ secrets.S3_ACCESS_KEY }} + aws_secret_access_key: ${{ secrets.S3_SECRET_KEY }} + aws_bucket: ${{ secrets.S3_BUCKET }} + endpoint: ${{ secrets.S3_ENDPOINT }} + source_dir: 'target/media' + destination_dir: 'updates/latest' + Clear_CloudFlare_Updates: + needs: [Upload_Updates_To_S3] + uses: ./.github/workflows/flush_cloudflare_cache.yml + secrets: inherit Upload_Artifacts: + continue-on-error: true + if: github.event_name != 'workflow_dispatch' needs: [Build_Executable, Build_Installers, Build_SPK, Build_DEB, Build_PKG, Build_Portable] permissions: contents: write @@ -91,6 +117,7 @@ jobs: with: asset_paths: '["target/media/PeerBanHelper_*", "target/media/peerbanhelper_*", "target/media/peerbanhelper-*"]' Build_Docker: + if: github.event_name != 'workflow_dispatch' permissions: contents: write checks: write @@ -119,7 +146,7 @@ jobs: password: ${{ secrets.DOCKER_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5.5.1 + uses: docker/metadata-action@v5.6.1 with: images: ghostchu/peerbanhelper tags: | @@ -132,7 +159,7 @@ jobs: type=raw,latest type=sha - name: Build and push Docker image - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: context: . file: ./Dockerfile @@ -157,7 +184,7 @@ jobs: password: ${{ secrets.ALIYUN_ACR_PASSWORD }} - name: Extract metadata (tags, labels) for Aliyun ACR id: meta-acr - uses: docker/metadata-action@v5.5.1 + uses: docker/metadata-action@v5.6.1 with: images: registry.cn-hangzhou.aliyuncs.com/ghostchu/peerbanhelper tags: | @@ -170,7 +197,7 @@ jobs: type=raw,latest type=sha - name: Build and push Aliyun ACR - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: context: . file: ./Dockerfile-Release diff --git a/README.EN.md b/README.EN.md index 29f13db833..f909d6e0b7 100644 --- a/README.EN.md +++ b/README.EN.md @@ -13,11 +13,11 @@ Following function are provided by PeerBanHelper: - [IP/GeoIP/IP type Blacklist](https://docs.pbh-btn.com/en/docs/module/ip-address-blocker) - [Fake progress checker (heuristic client detection)](https://docs.pbh-btn.com/en/docs/module/progress-cheat-blocker) - [Auto range ban](https://docs.pbh-btn.com/en/docs/module/auto-range-ban) -- [Multi-dail ban](https://docs.pbh-btn.com/en/docs/module/multi-dial) +- [Multi-dial ban](https://docs.pbh-btn.com/en/docs/module/multi-dial) - Peer ID/Client Name camouflage check, powered by [AviatorScript Engine](https://docs.pbh-btn.com/en/docs/module/expression-engine) - [Active monitoring(data analysis)](https://docs.pbh-btn.com/en/docs/module/active-monitoring) - [IP set subscribe](https://docs.pbh-btn.com/en/docs/module/ip-address-blocker-rules) -- a mordern WebUI +- A modern WebUI In addition, PeerBanHelper downloads the GeoIP library at startup, and supports the following functions once it successful loaded: - View IP address attribution, AS information (ASN, ISP, AS name, etc.), network type information (broadband, base station, IoT, data center, etc.) in the blocking list. @@ -34,8 +34,8 @@ In addition, PeerBanHelper downloads the GeoIP library at startup, and supports - BiglyBT([plugin](https://github.com/PBH-BTN/PBH-Adapter-BiglyBT) is required) - Deluge([plugin](https://github.com/PBH-BTN/PBH-Adapter-Deluge) is required) - Azureus(Vuze)([plugin](https://github.com/PBH-BTN/PBH-Adapter-Azureus) is required) -- Transmission **(deprected;3.00-20 or higher)** -- BitComet **v2.10 Beta6 [20240928] or higher** +- Transmission **(deprecated;3.00-20 or higher)** +- BitComet **v2.10 Beta6 [20240928] or higher** (P2SP LTSeed mode is not supported) # Screenshots @@ -51,7 +51,7 @@ Please read the [docs](https://docs.pbh-btn.com/en/docs/category/%E5%AE%89%E8%A3 ## FAQ -Before submit issue, please read the [FAQ](https://docs.pbh-btn.com/en/docs/faq) +Before submitting an issue, please read the [FAQ](https://docs.pbh-btn.com/en/docs/faq) ## Support Consider join our [Telegram](https://t.me/+_t3Nt5GZ6bJmYjBl) group. @@ -68,6 +68,12 @@ Any consequences caused by the user's use of this software are borne by the user [![Star History Chart](https://api.star-history.com/svg?repos=PBH-BTN/PeerBanHelper&type=Date)](https://star-history.com/#PBH-BTN/PeerBanHelper&Date) +### Install4j + +PeerBanHelper use [Install4j multi-platform installer builder](https://www.ej-technologies.com/products/install4j/overview.html) to build its multi-platform installer. Thanks the open-source license provided by ej-technolgies. Click the link or the image below to download install4j. + +[![Install4j](https://www.ej-technologies.com/images/product_banners/install4j_large.png)](https://www.ej-technologies.com/products/install4j/overview.html) + ## Credit ### Backend @@ -91,9 +97,3 @@ Any consequences caused by the user's use of this software are borne by the user - [Vue](https://vuejs.org/) - [ArcoDesign](https://arco.design/) - [ECharts](https://echarts.apache.org/en/index.html) - -### Install4j - -PeerBanHelper use [Install4j multi-platform installer builder](https://www.ej-technologies.com/products/install4j/overview.html) to build its multi-platform installer. Thanks the open-source license provided by ej-technolgies. Click the link or the image below to download install4j. - -[![Install4j](https://www.ej-technologies.com/images/product_banners/install4j_large.png)](https://www.ej-technologies.com/products/install4j/overview.html) diff --git a/README.md b/README.md index 4bd2e93521..a00ad1106b 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,9 @@ PeerBanHelper 主要由以下几个功能模块组成: - Deluge(需要安装[插件](https://github.com/PBH-BTN/PBH-Adapter-Deluge)) - Azureus(Vuze)(需要安装[插件](https://github.com/PBH-BTN/PBH-Adapter-Azureus)) - Transmission **(不建议使用;3.00-20 或更高版本)** -- BitComet **v2.10 Beta6 [20240928] 或更高版本** +- BitComet **v2.10 Beta6 [20240928] 或更高版本** (不支持 P2SP LTSeed 长效种子反吸血,因为 BitComet 暂时无法封禁长效连接) +PeerBanHelper 仅支持对传统 IPv4 或 IPv6 地址的反吸血,如遇 I2P 或者 Tor 连接将主动忽略。 # 截图 @@ -49,7 +50,6 @@ PeerBanHelper 主要由以下几个功能模块组成: 查看 [PeerBanHelper 文档](https://docs.pbh-btn.com/docs/category/%E5%AE%89%E8%A3%85%E9%83%A8%E7%BD%B2) - ## 常见问题 在报告问题前,请先检查 [常见问题列表](https://docs.pbh-btn.com/docs/faq) @@ -69,6 +69,13 @@ PeerBanHelper 主要由以下几个功能模块组成: [![Star History Chart](https://api.star-history.com/svg?repos=PBH-BTN/PeerBanHelper&type=Date)](https://star-history.com/#PBH-BTN/PeerBanHelper&Date) + +### Install4j + +PeerBanHelper 使用 [Install4j multi-platform installer builder](https://www.ej-technologies.com/products/install4j/overview.html) 打包多平台安装程序。感谢 ej-technolgies 的开放源代码许可证。点击链接或者下面的图片下载 install4j。 + +[![Install4j](https://www.ej-technologies.com/images/product_banners/install4j_large.png)](https://www.ej-technologies.com/products/install4j/overview.html) + ## Credit ### Backend @@ -93,8 +100,3 @@ PeerBanHelper 主要由以下几个功能模块组成: - [ArcoDesign](https://arco.design/) - [ECharts](https://echarts.apache.org/en/index.html) -### Install4j - -PeerBanHelper 使用 [Install4j multi-platform installer builder](https://www.ej-technologies.com/products/install4j/overview.html) 打包多平台安装程序。感谢 ej-technolgies 的开放源代码许可证。点击链接或者下面的图片下载 install4j。 - -[![Install4j](https://www.ej-technologies.com/images/product_banners/install4j_large.png)](https://www.ej-technologies.com/products/install4j/overview.html) diff --git a/docker-compose.yml b/docker-compose.yml index 193d45f678..34f6f87954 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,26 @@ -version: "3.9" services: peerbanhelper: - image: "ghostchu/peerbanhelper:<最新版本号>" + image: "ghostchu/peerbanhelper:<填写版本号>" restart: unless-stopped container_name: "peerbanhelper" volumes: - ./:/app/data ports: - "9898:9898" + stop_grace_period: 30s +# 如果不需要 IPFS 功能,请连带这段注释包括下面的所有内容删除 environment: - - PUID=0 - - PGID=0 - - TZ=UTC - stop_grace_period: 30s \ No newline at end of file + - PBH_KUBO_RPC=/dnsaddr/kubo/tcp/5001 + depends_on: + - kubo + links: + - kubo + kubo: + image: "ipfs/kubo:release" + restart: unless-stopped + container_name: "kubo" + ports: + - "4001:4001" + stop_grace_period: 30s + volumes: + - ./ipfs:/data/ipfs \ No newline at end of file diff --git a/install4j/lang/custom.utf8 b/install4j/lang/custom.utf8 index 02b2552258..5f79af16ff 100644 --- a/install4j/lang/custom.utf8 +++ b/install4j/lang/custom.utf8 @@ -1,8 +1,11 @@ launcher.peerbanhelper.gui=PeerBanHelper -launcher.peerbanhelper.gui.swing=PeerBanHelper(兼容模式) -launcher.peerbanhelper.nogui=PeerBanHelper(无GUI, 控制台) -launcher.peerbanhelper.service=PeerBanHelper(服务) +launcher.peerbanhelper.silent=PeerBanHelper (静默启动) +launcher.peerbanhelper.nogui=PeerBanHelper (无GUI, 控制台) +launcher.peerbanhelper.service=PeerBanHelper (服务) +launcher.peerbanhelper.updater=PeerBanHelper 检查更新 checkbox.followsystemstartup=登录时自动启动到系统托盘 peerbanhelper.description=PeerBanHelper checkbox.registersystemservice=注册为系统服务(除非你知道这是做什么的,否则不要选中) -systemservice.note=将 PeerBanHelper 安装为系统服务时,Windows 系统下配置文件将被存储在 C:\Windows\System32\config\systemprofile\AppData\Local\PeerBanHelper 位置,访问此位置需要您拥有管理员权限。\nPeerBanHelper 将在系统启动时自动启动。 \ No newline at end of file +systemservice.note=将 PeerBanHelper 安装为系统服务时,Windows 系统下配置文件将被存储在 C:\Windows\System32\config\systemprofile\AppData\Local\PeerBanHelper 位置,访问此位置需要您拥有管理员权限。\nPeerBanHelper 将在系统启动时自动启动。 +components.peerbanhelper=PeerBanHelper +components.peerbanhelper.description=PeerBanHelper 的核心主程序,包含了主程序代码和必要的运行文件 \ No newline at end of file diff --git a/install4j/lang/en-US.utf8 b/install4j/lang/en-US.utf8 index d306b0afb1..2b2b66cf42 100644 --- a/install4j/lang/en-US.utf8 +++ b/install4j/lang/en-US.utf8 @@ -1,8 +1,11 @@ launcher.peerbanhelper.gui=PeerBanHelper -launcher.peerbanhelper.gui.swing=PeerBanHelper(Compatibility Mode) -launcher.peerbanhelper.nogui=PeerBanHelper(NoGUI, Console) -launcher.peerbanhelper.service=PeerBanHelper(Service) +launcher.peerbanhelper.gui.swing=PeerBanHelper (Compatibility Mode) +launcher.peerbanhelper.nogui=PeerBanHelper (NoGUI, Console) +launcher.peerbanhelper.service=PeerBanHelper (Service) +launcher.peerbanhelper.updater=PeerBanHelper Updater checkbox.followsystemstartup=Boot automatically to the system tray when logged in peerbanhelper.description=PeerBanHelper checkbox.registersystemservice=Register as system service (DO NOT CHECK UNLESS YOU KNOW WHAT YOU'RE DOING) -systemservice.note=When you install PeerBanHelper as system service,under Windows operation system, the configuration and files will store at C:\Windows\System32\config\systemprofile\AppData\Local\PeerBanHelper. Access this location will require Administrator privileges. \ No newline at end of file +systemservice.note=When you install PeerBanHelper as system service,under Windows operation system, the configuration and files will store at C:\Windows\System32\config\systemprofile\AppData\Local\PeerBanHelper. Access this location will require Administrator privileges. +components.peerbanhelper=PeerBanHelper +components.peerbanhelper.description=PeerBanHelper core program, including main program code and necessary runtime files \ No newline at end of file diff --git a/install4j/lang/zh-CN.utf8 b/install4j/lang/zh-CN.utf8 index 6206ed964d..c7dea51ac4 100644 --- a/install4j/lang/zh-CN.utf8 +++ b/install4j/lang/zh-CN.utf8 @@ -1,6 +1,9 @@ launcher.peerbanhelper.gui=PeerBanHelper -launcher.peerbanhelper.gui.swing=PeerBanHelper(兼容模式) -launcher.peerbanhelper.nogui=PeerBanHelper(无GUI, 控制台) -launcher.peerbanhelper.service=PeerBanHelper(服务) +launcher.peerbanhelper.silent=PeerBanHelper (静默启动) +launcher.peerbanhelper.nogui=PeerBanHelper (无GUI, 控制台) +launcher.peerbanhelper.service=PeerBanHelper (服务) +launcher.peerbanhelper.updater=PeerBanHelper 检查更新 checkbox.followsystemstartup=登录到桌面时自动启动 -peerbanhelper.description=一个能够自动封禁不受欢迎、吸血和异常的 Peers,并支持自定义规则的 BT 客户端辅助工具 \ No newline at end of file +peerbanhelper.description=一个能够自动封禁不受欢迎、吸血和异常的 Peers,并支持自定义规则的 BT 客户端辅助工具 +components.peerbanhelper=PeerBanHelper +components.peerbanhelper.description=PeerBanHelper 的核心主程序,包含了主程序代码和必要的运行文件 \ No newline at end of file diff --git a/install4j/project.install4j b/install4j/project.install4j index c196918a63..a7c285a769 100644 --- a/install4j/project.install4j +++ b/install4j/project.install4j @@ -1,7 +1,7 @@ - - + + @@ -23,13 +23,21 @@ + + + ${i18n:components.peerbanhelper.description} + + + + + - + - + @@ -39,11 +47,11 @@ - + - + @@ -53,11 +61,11 @@ - + - + @@ -71,7 +79,7 @@ - + @@ -81,8 +89,22 @@ + + + + + + + + + + + + + + - + @@ -353,12 +375,27 @@ return true; + + + + 26 + + context.getBooleanVariable("executeLauncherAction") && (!context.isUnattended()) + + ${form:finishedMessage} + + + ${i18n:RunEntryExec("${compiler:sys.fullName}")} + + executeLauncherAction + + @@ -426,6 +463,881 @@ return console.askYesNo(message, true); + + + + + + ${compiler:sys.install4jHome}/resource/updater_16.png + + + + + ${compiler:sys.install4jHome}/resource/updater_32.png + + + + + ${compiler:sys.install4jHome}/resource/updater_48.png + + + + + ${compiler:sys.install4jHome}/resource/updater_128.png + + + + + ${compiler:sys.install4jHome}/resource/updater_256.png + + + + bgupdater + + -Dapple.awt.UIElement=true + ${compiler:sys.fullName} + + + + + + + + + import java.nio.file.*; + +Path dir = context.getInstallationDirectory().toPath(); +// quit if the current installation is on a read only file system, for example a disk image on macOS +// or if the directory is not writeable on Linux/Unix. +// If there is no "Request privileges" action in the installer, the condition should also +// check Files.isWritable(dir) +return !Files.getFileStore(dir).isReadOnly() && ((Util.isWindows() && !Util.isArchive()) || Util.isMacOS() || (Util.isLinux() && !Util.isArchive())); + + + + + + + + ${installer:updatesUrl?:${compiler:sys.updatesUrl}} + updateDescriptor + + + + + + + + UpdateDescriptorEntry entry = ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry(); + +if (entry == null) { + return null; +} else if (entry.isArchive() && !entry.isSingleBundle()) { + // only installers and single bundle archives on macOS are supported + return null; +} else if (entry.isDownloaded()) { + // update has been downloaded already + return null; +} else { + return entry; +} + + + updateDescriptorEntry + + + + + + + context.getVariable("updateDescriptorEntry") != null + + + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion() + + + updaterNewVersion + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm() + + + updaterDownloadUrl + + + + + + + context.getVariable("sys.updateStorageDir") + File.separator + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName() + + + updaterDownloadFile + + + + + + + ${installer:updaterDownloadFile} + + + ${installer:updaterDownloadUrl} + + + + + + + !((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() + + + + + + + + + + ${installer:updaterDownloadFile} + + + + 755 + + + + + + + ${installer:updaterDownloadFile} + + + ${installer:updaterNewVersion} + + + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() + + + + + + + + + String dirName = context.getVariable("updaterDownloadFile") + "_dir"; +new File(dirName).mkdirs(); +return dirName; + + + updaterStagingDir + + + + + + + + + ${installer:updaterStagingDir} + + + + + + new File((String)context.getVariable("updaterStagingDir")).exists() + + + + + + ${installer:updaterDownloadFile} + + + + + ${installer:updaterStagingDir} + + + + + // only extract app bundle, no other top level files + +import com.install4j.api.unix.UnixFileSystem; + +File realFile = new File(dmgMountPoint, file.getPath()); + +return file.getParent() != null || (file.getName().endsWith(".app") && realFile.isDirectory() && !UnixFileSystem.getFileInformation(realFile).isLink()); + + + + ((String)context.getVariable("updaterDownloadFile")).endsWith(".dmg") + + + + + + ${installer:updaterDownloadFile} + + + + + ${installer:updaterStagingDir} + + + + + // only extract app bundle, no other top level files +file.getParent() != null || (file.getName().endsWith(".app") && directory) + + + + !((String)context.getVariable("updaterDownloadFile")).endsWith(".dmg") + + + + + + ${installer:updaterStagingDir} + + + ${installer:updaterNewVersion} + + + + + + + + + ${installer:updaterDownloadFile} + + + + + + + + + + + + + + + + + + + ${compiler:sys.install4jHome}/resource/updater_16.png + + + + + ${compiler:sys.install4jHome}/resource/updater_32.png + + + + + ${compiler:sys.install4jHome}/resource/updater_48.png + + + + + ${compiler:sys.install4jHome}/resource/updater_128.png + + + + + ${compiler:sys.install4jHome}/resource/updater_256.png + + + + updater + + ${i18n:updater.WindowTitle("${compiler:sys.fullName}")} + + + + + + + + + + + ${i18n:updater.WelcomeTitle("${compiler:sys.fullName}")} + + + + + + ${i18n:updater.WelcomeInfoText("${compiler:sys.fullName}")} + + !context.isConsole() + + labelText + + + + + + + ${i18n:updater.CheckForUpdateSubtitle} + ${i18n:updater.CheckForUpdateTitle} + + context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false); +context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false); +context.goForward(1, true, true); + + + + + + + + + + ${installer:updatesUrl?:${compiler:sys.updatesUrl}} + updateDescriptor + + + + + + + + ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry() + + + updateDescriptorEntry + + + + + + + context.getVariable("updateDescriptorEntry") != null + + + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion() + + + updaterNewVersion + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileSizeVerbose() + + + updaterDownloadSize + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getComment() + + + updaterComment + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm() + + + updaterDownloadUrl + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() ? Boolean.TRUE : Boolean.FALSE + + + isArchive + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName().toLowerCase().endsWith(".dmg") + + + isDmg + + + + + + + + + ${i18n:updater.CheckForUpdateLabel} + + + statusVisible + initialStatusMessage + + + + + + + + + context.getVariable("updateDescriptorEntry") == null + + + + + + + ${i18n:updater.UpToDateTitle} + + + + + + ${i18n:updater.UpToDateInfoText("${compiler:sys.fullName}")} + + !context.isConsole() + + labelText + + + + + + + + + + + context.getVariable("updateDescriptorEntry") != null + + + + + + + ${i18n:updater.NewVersionAvailableSubtitle("${compiler:sys.fullName}")} + ${i18n:updater.NewVersionAvailableTitle} + + !context.getBooleanVariable("skipNewVersionAvailable") + + + + ${i18n:updater.CurrentVersionLabel} + + + 128 + 0 + 0 + 255 + + + + + ${installer:sys.version} + + + + + + + ${i18n:updater.NewVersionLabel} + + + 0 + 128 + 0 + 255 + + + + + ${installer:updaterNewVersion} + + + + + + + context.goForward(1, false, false); + + + ${i18n:updater.ShowComments} + + ((String)context.getVariable("updaterComment")).length() > 0 + + + + + + + ${i18n:updater.DownloadLocationLabel} + + + + + ${installer:sys.downloadsDir} + ${i18n:updater.DownloadToLabel} + + updaterDownloadLocation + + + + + ${i18n:updater.DownloadSizeLabel} + ${installer:updaterDownloadSize} + + + + + + + + ${i18n:updater.CommentsSubTitle} + ${i18n:updater.CommentsTitle} + + false // This screen is only shown if the user clicks the "Show comments" hyperlink label in the previous screen. + + if (context.isConsole()) { + context.goBackInHistory(1); +} +return true; + WizardContext wizardContext = context.getWizardContext(); +wizardContext.setControlButtonVisible(ControlButtonType.NEXT, false); +wizardContext.setControlButtonVisible(ControlButtonType.CANCEL, false); + + + + + ${i18n:updater.CommentsLabel} + + !context.isConsole() + + labelText + + + + + ${installer:updaterComment} + + + + + textSource + displayedText + displayedTextFile + variableName + + + + + + + console.waitForEnter(); +return true; + + + + + + + + + + ${i18n:updater.DownloadSubTitle} + ${i18n:updater.DownloadTitle} + + context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false); +context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false); +context.goForward(1, true, true); + + + + + + + context.getVariable("updaterDownloadLocation") + File.separator + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName() + + + updaterDownloadFile + + + + + + + ${installer:updaterDownloadFile} + + + ${installer:updaterDownloadUrl} + + + + + + + + ${installer:updaterDownloadFile} + + + + 755 + + + + + + + statusVisible + initialStatusMessage + + + + + + + + + ${i18n:updater.FinishTitle} + + !(context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg")) + + + + + + !context.getBooleanVariable("isArchive") && ((Integer)context.getVariable("updaterLaunchSelection")).intValue() == 0 + + + + + + + + + List<String> args = new ArrayList<String>(); +String installationDirectory = context.getInstallationDirectory().getPath(); +if (context.isUnattended()) { + args.add("-q"); + args.add("-wait"); + args.add("20"); + ProgressInterface progressInterface = context.getProgressInterface(); + if (progressInterface.isUnattendedProgressDialog()) { + if (progressInterface.isAlertsShown()) { + args.add("-alerts"); + } + args.add("-splash"); + args.add("Installing"); + } +} else if (context.isConsole()) { + args.add("-c"); +} + args.add("-dir"); + args.add(installationDirectory); + + return args.toArray(new String[args.size()]); + + + + installerArguments + + + + + + + ${installer:installerArguments} + + + + ${installer:updaterDownloadFile} + + + + + ${installer:updaterDownloadLocation} + + + + + + + + + + + + + !context.isConsole() + + labelText + + + + + + ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} + + !context.isConsole() + + labelText + + + + + ${i18n:updater.LaunchUpdaterQuestion} + + + + + + + + + + + ${i18n:updater.LaunchUpdaterLabel} + ${i18n:updater.DoNotLaunchUpdaterLabel} + + updaterLaunchSelection + + !context.getBooleanVariable("isArchive") + + + + + + Util.showPath((String)context.getVariable("updaterDownloadFile")); + + + ${i18n:updater.OpenContainingFolderLabel} + + !context.isConsole() + + + + + + + + + + + + ${i18n:updater.FinishTitle} + + context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg") + + + + + + context.getBooleanVariable("updaterOpenDmg") + + + + + + + + + + Util.showPath((String)context.getVariable("updaterDownloadFile")); +return true; + + + + + + + + + + + + + !context.isConsole() + + labelText + + + + + + ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} + + !context.isConsole() + + labelText + + + + + ${i18n:updater.LaunchUpdaterQuestion} + + + + + + + + + + ${i18n:updater.OpenContainingFolderLabel} + + updaterOpenDmg + + + + + + + + + + + + + + + + diff --git a/webui/src/components/iconFont.ts b/webui/src/components/iconFont.ts index cda2e1ef87..cc5cc0d03d 100644 --- a/webui/src/components/iconFont.ts +++ b/webui/src/components/iconFont.ts @@ -2,7 +2,7 @@ import { Icon } from '@arco-design/web-vue' import { defineComponent, h } from 'vue' const IconFont = Icon.addFromIconFontCn({ - src: 'https://at.alicdn.com/t/c/font_4646549_xrh0h0mmvwj.js' + src: 'https://at.alicdn.com/t/c/font_4646549_kvl69xg55a.js' }) export function genIconComponent(type: string) { diff --git a/webui/src/components/pageFooter.vue b/webui/src/components/pageFooter.vue index ac1472538d..63aeed37be 100644 --- a/webui/src/components/pageFooter.vue +++ b/webui/src/components/pageFooter.vue @@ -16,7 +16,10 @@
{{ serverVersion?.version }}

- {{ t(status?.activated ? 'plus.status.activated' : 'plus.status.inactive') }} + {{ + t( + status?.activated + ? status.keyData?.type === LicenseType.LicenseLocal + ? 'plus.status.activated.local' + : 'plus.status.activated' + : 'plus.status.inactive' + ) + }} @@ -26,6 +34,12 @@ {{ status?.keyData?.licenseTo }} + + {{ t('plus.type.' + status?.keyData?.type) }} + {{ d(status?.keyData?.createAt ?? 0, 'long') }} @@ -40,29 +54,51 @@ - {{ t('plus.begging') }} + {{ + t( + status?.keyData?.type === LicenseType.LicenseLocal + ? 'plug.begging.local' + : 'plus.begging' + ) + }} support us! - - {{ t('plus.activeTips') }} - - + + + + import medal from '@/components/plusMedal.vue' +import { obtainFreeTrial } from '@/service/version' import { useEndpointStore } from '@/stores/endpoint' import { Message } from '@arco-design/web-vue' import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' +import { LicenseType } from '@/api/model/manifest' const { t, d } = useI18n() const endpointStore = useEndpointStore() const showModal = ref(false) @@ -104,4 +142,18 @@ const submitKey = async (key: string) => { loading.value = false } } +const handleTry = async () => { + loading.value = true + try { + const res = await obtainFreeTrial() + if (res.success) { + Message.success(res.message) + endpointStore.getPlusStatus() + } else throw new Error(res.message) + } catch (e: unknown) { + if (e instanceof Error) Message.error(e.message) + } finally { + loading.value = false + } +} diff --git a/webui/src/locale/en-US.ts b/webui/src/locale/en-US.ts index 101cca6e4b..67db062af7 100644 --- a/webui/src/locale/en-US.ts +++ b/webui/src/locale/en-US.ts @@ -8,10 +8,12 @@ import oobeLocale from '@/views/oobe/locale/en-US' import topBanPageLocale from '@/views/ranks/locale/en-US' import ruleManageMentLocale from '@/views/rule-management/locale/en-US' import configLocale from '@/views/settings/locale/en-US' +import scriptLocale from '@/views/custom-script/locale/en-US' import alertLocale from './en-US/alert' import copierLocale from './en-US/copier' import plusLocale from './en-US/plus' import settingsLocale from './en-US/settings' + export default { 'navbar.action.locale': 'Switch to English', 'navbar.action.autoUpdate': 'Auto Update', @@ -36,6 +38,7 @@ export default { 'router.metrics.charts': 'Charts', 'router.rule_management': 'Rule', 'router.config': 'Preferences', + 'router.script': 'Custom Script', 'router.moduleNotEnable': '{moduleName} is not enabled', 'router.moduleNotEnable.tips': 'Please enable the feature in the configuration file', @@ -62,5 +65,6 @@ export default { ...chartsLocale, ...configLocale, ...alertLocale, - ...copierLocale + ...copierLocale, + ...scriptLocale } diff --git a/webui/src/locale/en-US/plus.ts b/webui/src/locale/en-US/plus.ts index 72129d72e6..2e6c760d53 100644 --- a/webui/src/locale/en-US/plus.ts +++ b/webui/src/locale/en-US/plus.ts @@ -2,14 +2,23 @@ export default { 'plus.status': 'Status', 'plus.subscription': 'Subscription', 'plus.status.activated': 'Activated, thanks for supporting us :)', + 'plus.status.activated.local': 'Activated', 'plus.status.inactive': 'Inactive', 'plus.key': 'License', + 'plus.type': 'Type', + 'plus.type.local': 'Local', + 'plus.type.afdian': 'Aifadian', 'plus.licenseTo': 'License to', 'plus.startAt': 'Issued on', 'plus.expireAt': 'Expire on', 'plus.description': 'Description', 'plus.begging': "PeerBanHelper is an open source project that a group of like-minded people use to support PeerBanHelper's development. In order to better support the development of PeerBanHelper, some advanced features need to be unlocked after donation. Please buy us a cup of coffee!", + 'plug.begging.local': + ' PeerBanHelper is a non-profit, open-source free software. The free license authorizes personal use on this computer. You may not resell it or use it to violate the terms of service. If PeerBanHelper has been helpful to you, please consider supporting us on Afdian', 'plus.activeTips': 'Already donated? Input your license here to activate your subscription', - 'plus.activeSuccess': 'Activation successful' + 'plus.activeSuccess': 'Activation successful', + + 'plus.or': 'Or', + 'plus.try': "I don't want to donate" } diff --git a/webui/src/locale/zh-CN.ts b/webui/src/locale/zh-CN.ts index 4ef6215347..e7f12c8265 100644 --- a/webui/src/locale/zh-CN.ts +++ b/webui/src/locale/zh-CN.ts @@ -8,6 +8,7 @@ import oobeLocale from '@/views/oobe/locale/zh-CN' import topBanPageLocale from '@/views/ranks/locale/zh-CN' import ruleManageMentLocale from '@/views/rule-management/locale/zh-CN' import configLocale from '@/views/settings/locale/zh-CN' +import scriptLocale from '@/views/custom-script/locale/zh-CN' import alertLocale from './zh-CN/alert' import copierLocale from './zh-CN/copier' import plusLocale from './zh-CN/plus' @@ -36,6 +37,7 @@ export default { 'router.metrics.charts': '图表', 'router.rule_management': '规则管理', 'router.config': '设置', + 'router.script': '自定义脚本', 'router.moduleNotEnable': '{moduleName}功能未启用', 'router.moduleNotEnable.tips': '请在配置文件中开启相关功能', @@ -62,5 +64,6 @@ export default { ...chartsLocale, ...configLocale, ...alertLocale, - ...copierLocale + ...copierLocale, + ...scriptLocale } diff --git a/webui/src/locale/zh-CN/plus.ts b/webui/src/locale/zh-CN/plus.ts index 9adf338f35..d335300a4f 100644 --- a/webui/src/locale/zh-CN/plus.ts +++ b/webui/src/locale/zh-CN/plus.ts @@ -2,14 +2,23 @@ export default { 'plus.status': '状态', 'plus.subscription': '当前订阅', 'plus.status.activated': '已激活,感谢支持 :)', + 'plus.status.activated.local': '已激活', 'plus.status.inactive': '未激活', 'plus.key': '许可证', + 'plus.type': '类型', + 'plus.type.local': '本地', + 'plus.type.afdian': '爱发电', 'plus.licenseTo': '授权给', 'plus.startAt': '开始时间', 'plus.expireAt': '过期时间', 'plus.description': '说明', 'plus.begging': 'PeerBanHelper 是一群志同道合的人共同用爱发电的一个开源项目,为了更好地支持 PeerBanHelper 的发展,部分高级功能需要捐赠后解锁,请给我们买一杯咖啡吧!', + 'plug.begging.local': + 'PeerBanHelper 是一个非营利,开放源代码的免费软件。免费许可证授权这台计算机的个人用途,您不得重新出售或者用于违反服务条款的用途。如果 PeerBanHelper 帮助到了你,请考虑在爱发电上捐赠支持我们:', 'plus.activeTips': '已经捐赠?在这里输入您的许可证以激活您的订阅', - 'plus.activeSuccess': '激活成功' + 'plus.activeSuccess': '激活成功', + + 'plus.or': '或者', + 'plus.try': '我不想捐赠' } diff --git a/webui/src/router/index.ts b/webui/src/router/index.ts index d3fb950a0b..45600787cf 100644 --- a/webui/src/router/index.ts +++ b/webui/src/router/index.ts @@ -2,7 +2,7 @@ import { genIconComponent } from '@/components/iconFont' import BanList from '@/views/banlist/index.vue' import GenericBlackList from '@/views/rule-management/components/generic/index.vue' import SubscribeManagement from '@/views/rule-management/components/subscribe/index.vue' -import { IconCloud, IconLocation, IconStorage } from '@arco-design/web-vue/es/icon' +import { IconCloud, IconCodeSquare, IconLocation, IconStorage } from '@arco-design/web-vue/es/icon' import { computed, h } from 'vue' import { createRouter, @@ -88,6 +88,16 @@ export const routerOptions: RouteRecordRaw[] = [ }, component: SubscribeManagement }, + { + path: '/script', + name: 'rule_management_script', + meta: { + label: 'router.script', + icon: () => h(IconCodeSquare), + needLogin: true + }, + component: () => import('@/views/custom-script/index.vue') + }, { path: '/ruleIp', name: 'rule_management_ip', diff --git a/webui/src/service/downloaders.ts b/webui/src/service/downloaders.ts index cedbc6e2d6..5aa9b3541d 100644 --- a/webui/src/service/downloaders.ts +++ b/webui/src/service/downloaders.ts @@ -1,3 +1,4 @@ +import type { CommonResponse, CommonResponseWithoutData } from '@/api/model/common' import type { ClientStatus, CreateDownloadRequest, @@ -9,13 +10,12 @@ import type { Statistic } from '@/api/model/statistic' import { useEndpointStore } from '@/stores/endpoint' import urlJoin from 'url-join' import { getCommonHeader } from './utils' -import type { CommonResponse, CommonResponseWithoutData } from '@/api/model/common' export async function getClientStatus(name: string): Promise> { const endpointStore = useEndpointStore() await endpointStore.serverAvailable const url = new URL( - urlJoin(endpointStore.endpoint, `api/downloaders/${name}/status`), + urlJoin(endpointStore.endpoint, `api/downloaders/${encodeURIComponent(name)}/status`), location.href ) return fetch(url, { headers: getCommonHeader() }).then((res) => { @@ -50,7 +50,7 @@ export async function getTorrents(downloader: string): Promise { @@ -79,7 +79,10 @@ export async function UpdateDownloader( ): Promise { const endpointStore = useEndpointStore() await endpointStore.serverAvailable - const url = new URL(urlJoin(endpointStore.endpoint, `/api/downloaders/${target}`), location.href) + const url = new URL( + urlJoin(endpointStore.endpoint, `/api/downloaders/${encodeURIComponent(target)}`), + location.href + ) return fetch(url, { method: 'PATCH', headers: getCommonHeader(), @@ -107,7 +110,10 @@ export async function TestDownloaderConfig( export async function DeleteDownloader(name: string): Promise { const endpointStore = useEndpointStore() await endpointStore.serverAvailable - const url = new URL(urlJoin(endpointStore.endpoint, `/api/downloaders/${name}`), location.href) + const url = new URL( + urlJoin(endpointStore.endpoint, `/api/downloaders/${encodeURIComponent(name)}`), + location.href + ) return fetch(url, { method: 'DELETE', headers: getCommonHeader() }).then((res) => { endpointStore.assertResponseLogin(res) return res.json() @@ -121,7 +127,10 @@ export async function getPeer( const endpointStore = useEndpointStore() await endpointStore.serverAvailable const url = new URL( - urlJoin(endpointStore.endpoint, `/api/downloaders/${downloader}/torrent/${torrentId}/peers`), + urlJoin( + endpointStore.endpoint, + `/api/downloaders/${encodeURIComponent(downloader)}/torrent/${torrentId}/peers` + ), location.href ) return fetch(url, { headers: getCommonHeader() }).then((res) => { diff --git a/webui/src/service/labs.ts b/webui/src/service/labs.ts new file mode 100644 index 0000000000..6331bc2a89 --- /dev/null +++ b/webui/src/service/labs.ts @@ -0,0 +1,37 @@ +import type { CommonResponse, CommonResponseWithoutData } from '@/api/model/common' +import type { Experiment } from '@/api/model/labs' +import { useEndpointStore } from '@/stores/endpoint' +import urlJoin from 'url-join' +import { getCommonHeader } from './utils' + +export async function GetExperimentList(): Promise> { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + + const url = new URL(urlJoin(endpointStore.endpoint, 'api/laboratory/experiments'), location.href) + return fetch(url, { headers: getCommonHeader() }).then((res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} + +export async function SetExperimentStatus( + id: string, + status: boolean +): Promise { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + + const url = new URL( + urlJoin(endpointStore.endpoint, `api/laboratory/experiment/${id}`), + location.href + ) + return fetch(url, { + method: 'PUT', + headers: getCommonHeader(), + body: JSON.stringify({ status }) + }).then((res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} diff --git a/webui/src/service/push.ts b/webui/src/service/push.ts new file mode 100644 index 0000000000..5d5183d9b8 --- /dev/null +++ b/webui/src/service/push.ts @@ -0,0 +1,79 @@ +import type { CommonResponse, CommonResponseWithoutData } from '@/api/model/common' +import type { PushConfig } from '@/api/model/push' +import { useEndpointStore } from '@/stores/endpoint' +import urlJoin from 'url-join' +import { getCommonHeader } from './utils' + +export async function GetPushChannelList(): Promise> { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + const url = new URL(urlJoin(endpointStore.endpoint, `api/push`), location.href) + return fetch(url, { headers: getCommonHeader() }).then((res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} + +export async function CreatePushChannel(channel: PushConfig): Promise { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + const url = new URL(urlJoin(endpointStore.endpoint, `api/push`), location.href) + return fetch(url, { + method: 'PUT', + headers: getCommonHeader(), + body: JSON.stringify(channel) + }).then(async (res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} + +export async function UpdatePushChannel( + target: string, + channel: PushConfig +): Promise { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + const url = new URL( + urlJoin(endpointStore.endpoint, `api/push/${encodeURIComponent(target)}`), + location.href + ) + return fetch(url, { + method: 'PATCH', + headers: getCommonHeader(), + body: JSON.stringify(channel) + }).then(async (res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} + +export async function TestPushChannel(channel: PushConfig): Promise { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + const url = new URL(urlJoin(endpointStore.endpoint, `api/push/test`), location.href) + return fetch(url, { + method: 'POST', + headers: getCommonHeader(), + body: JSON.stringify(channel) + }).then(async (res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} + +export async function DeletePushChannel(name: string): Promise { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + const url = new URL( + urlJoin(endpointStore.endpoint, `api/push/${encodeURIComponent(name)}`), + location.href + ) + return fetch(url, { + method: 'DELETE', + headers: getCommonHeader() + }).then(async (res) => { + endpointStore.assertResponseLogin(res) + return res.json() + }) +} diff --git a/webui/src/service/script.ts b/webui/src/service/script.ts index 7465f6726e..d16e27f2bd 100644 --- a/webui/src/service/script.ts +++ b/webui/src/service/script.ts @@ -3,7 +3,7 @@ import type { CommonResponseWithoutData, CommonResponseWithPage } from '@/api/model/common' -import type { Script } from '@/api/model/script' +import type { EditableResult, Script } from '@/api/model/script' import { useEndpointStore } from '@/stores/endpoint' import urlJoin from 'url-join' import { getCommonHeader } from './utils' @@ -47,6 +47,19 @@ export async function GetScriptContent(id: string): Promise> { + const endpointStore = useEndpointStore() + await endpointStore.serverAvailable + + const url = new URL( + urlJoin(endpointStore.endpoint, `api/expression-engine/editable`), + location.href + ) + return fetch(url, { headers: getCommonHeader() }).then((res) => { + return res.json() + }) +} + export async function DeleteScript(id: string): Promise { const endpointStore = useEndpointStore() await endpointStore.serverAvailable diff --git a/webui/src/service/version.ts b/webui/src/service/version.ts index c3dae70202..7db9826cf0 100644 --- a/webui/src/service/version.ts +++ b/webui/src/service/version.ts @@ -55,6 +55,20 @@ export function setPHBPlusKey(key: string): Promise { }) } +export function obtainFreeTrial(): Promise { + const url = new URL( + urlJoin(useEndpointStore().endpoint, 'api/pbhplus/renewFreeLicense'), + location.href + ) + return fetch(url, { + method: 'POST', + headers: getCommonHeader() + }).then((res) => { + useEndpointStore().assertResponseLogin(res) + return res.json() + }) +} + export function getManifest(endpoint = useEndpointStore().endpoint): Promise { const url = new URL(urlJoin(endpoint, '/api/metadata/manifest'), location.href) return ( diff --git a/webui/src/stores/endpoint.ts b/webui/src/stores/endpoint.ts index a19485ff2f..d97c2d0c72 100644 --- a/webui/src/stores/endpoint.ts +++ b/webui/src/stores/endpoint.ts @@ -185,6 +185,7 @@ export const useEndpointStore = defineStore('endpoint', () => { setAuthToken, plusStatus, setPlusKey, + getPlusStatus, emitter: emitter, assertResponseLogin: (res: Response) => { if (res.status === 403) { diff --git a/webui/src/stores/userStore.ts b/webui/src/stores/userStore.ts index 6873f983f9..217db76e55 100644 --- a/webui/src/stores/userStore.ts +++ b/webui/src/stores/userStore.ts @@ -4,12 +4,30 @@ import { defineStore } from 'pinia' export const useUserStore = defineStore('userStore', () => { const licenseVersion = useStorage('userStore.licenseVersion', 0) const scriptWarningConfirmed = useStorage('userStore.scriptWarningConfirmed', false) + const showCharts = useStorage('userStore.showCharts', { + banTrends: true, + fieldPie: true, + ispPie: true, + traffic: true, + trends: true + }) const confirmScriptWarning = () => { scriptWarningConfirmed.value = true } + const setShowCharts = (v: { + banTrends: boolean + fieldPie: boolean + ispPie: boolean + traffic: boolean + trends: boolean + }) => { + showCharts.value = v + } return { licenseVersion, scriptWarningConfirm: scriptWarningConfirmed, - confirmScriptWarning + showCharts, + confirmScriptWarning, + setShowCharts } }) diff --git a/webui/src/views/banlist/components/banList.vue b/webui/src/views/banlist/components/banList.vue index e8865c07b7..e42e4123f8 100644 --- a/webui/src/views/banlist/components/banList.vue +++ b/webui/src/views/banlist/components/banList.vue @@ -13,9 +13,11 @@ @@ -44,15 +46,15 @@ diff --git a/webui/src/views/charts/index.vue b/webui/src/views/charts/index.vue index af2e2a196d..fccce2ab79 100644 --- a/webui/src/views/charts/index.vue +++ b/webui/src/views/charts/index.vue @@ -1,90 +1,57 @@ diff --git a/webui/src/views/settings/components/config/components/push/forms/mailForm.vue b/webui/src/views/settings/components/config/components/push/forms/mailForm.vue new file mode 100644 index 0000000000..8e506499ba --- /dev/null +++ b/webui/src/views/settings/components/config/components/push/forms/mailForm.vue @@ -0,0 +1,90 @@ + + diff --git a/webui/src/views/settings/components/config/components/push/forms/pushplusForm.vue b/webui/src/views/settings/components/config/components/push/forms/pushplusForm.vue new file mode 100644 index 0000000000..35feb752dc --- /dev/null +++ b/webui/src/views/settings/components/config/components/push/forms/pushplusForm.vue @@ -0,0 +1,31 @@ + + diff --git a/webui/src/views/settings/components/config/components/push/forms/serverchanForm.vue b/webui/src/views/settings/components/config/components/push/forms/serverchanForm.vue new file mode 100644 index 0000000000..2ce24c46d7 --- /dev/null +++ b/webui/src/views/settings/components/config/components/push/forms/serverchanForm.vue @@ -0,0 +1,16 @@ + + diff --git a/webui/src/views/settings/components/config/components/push/forms/telegramForm.vue b/webui/src/views/settings/components/config/components/push/forms/telegramForm.vue new file mode 100644 index 0000000000..cef1e17fd4 --- /dev/null +++ b/webui/src/views/settings/components/config/components/push/forms/telegramForm.vue @@ -0,0 +1,13 @@ + + diff --git a/webui/src/views/settings/components/config/components/push/pushCard.vue b/webui/src/views/settings/components/config/components/push/pushCard.vue new file mode 100644 index 0000000000..74c367aec0 --- /dev/null +++ b/webui/src/views/settings/components/config/components/push/pushCard.vue @@ -0,0 +1,98 @@ + + + diff --git a/webui/src/views/settings/components/config/index.vue b/webui/src/views/settings/components/config/index.vue index 9887ef437c..7a01c9f20e 100644 --- a/webui/src/views/settings/components/config/index.vue +++ b/webui/src/views/settings/components/config/index.vue @@ -55,6 +55,8 @@ + +
@@ -83,6 +85,7 @@ import lookup from './components/lookup.vue' import performance from './components/performance.vue' import persist from './components/persist.vue' import proxy from './components/proxy.vue' +import push from './components/push.vue' import webui from './components/webui.vue' const endpointStore = useEndpointStore() diff --git a/webui/src/views/settings/components/config/locale/en-US.ts b/webui/src/views/settings/components/config/locale/en-US.ts index 002a05e536..7e2f105e5f 100644 --- a/webui/src/views/settings/components/config/locale/en-US.ts +++ b/webui/src/views/settings/components/config/locale/en-US.ts @@ -51,7 +51,7 @@ export default { 'Are you sure you want to enable submit?', 'page.settings.tab.config.btn.allowScript': 'Allow BTN server push scripts', 'page.settings.tab.config.btn.allowScript.warning': - 'Warning, this means that the remote server can execute any code on your device, please enable with caution!', + 'Warning: this means that the remote server can execute any code on your device, please enable with caution!', 'page.settings.tab.config.btn.allowScript.tips': 'This option will allow BTN server push scripts to your device, this may increase the accuracy of the ban', @@ -75,5 +75,44 @@ export default { 'page.settings.tab.config.performance.title': 'Performance', 'page.settings.tab.config.performance.useEcoQOS': 'Use Windows EcoQos API', 'page.settings.tab.config.performance.useEcoQOS.tips': - 'Enable {link} on Windows Platform for power saving, the program performance will reduce and cronjobs may delay' + 'Enable {link} on Windows Platform for power saving, the program performance will reduce and cronjobs may delay', + + 'page.settings.tab.config.push.title': 'Message Push', + 'page.settings.tab.config.push.description': + 'By configuring message push, you can receive message push from PBH', + 'page.settings.tab.config.push.add': 'New', + 'page.settings.tab.config.push.edit': 'Edit', + 'page.settings.tab.config.push.deleteConfirm': 'Are you sure to delete?', + 'page.settings.tab.config.push.form.title.new': 'New push channel', + 'page.settings.tab.config.push.form.title.edit': 'Edit push channel', + 'page.settings.tab.config.push.form.name': 'Name', + 'page.settings.tab.config.push.form.name.placeholder': 'Enter a unique name', + 'page.settings.tab.config.push.form.type': 'Type', + 'page.settings.tab.config.push.form.type.smtp': 'E-Mail', + 'page.settings.tab.config.push.form.type.pushplus': 'Push+', + 'page.settings.tab.config.push.form.type.serverchan': 'ServerChan', + 'page.settings.tab.config.push.form.type.telegram': 'Telegram', + + 'page.settings.tab.config.push.form.stmp.host': 'Host', + 'page.settings.tab.config.push.form.stmp.port': 'Port', + 'page.settings.tab.config.push.form.stmp.sender': 'Sender', + 'page.settings.tab.config.push.form.stmp.senderName': 'Sender Name', + 'page.settings.tab.config.push.form.stmp.auth': 'Enable Auth', + 'page.settings.tab.config.push.form.stmp.authInfo': 'Auth Info', + 'page.settings.tab.config.push.form.stmp.username': 'Username', + 'page.settings.tab.config.push.form.stmp.password': 'Password', + 'page.settings.tab.config.push.form.stmp.encryption': 'Encryption', + 'page.settings.tab.config.push.form.stmp.receivers': 'Receivers', + 'page.settings.tab.config.push.form.stmp.receivers.placeholder': + "Enter one receivers, then press 'Enter'", + 'page.settings.tab.config.push.form.stmp.sendPartial': 'Send Partial', + + 'page.settings.tab.config.push.form.pushplus.token': 'Token', + 'page.settings.tab.config.push.form.pushplus.topic': 'Topic', + 'page.settings.tab.config.push.form.pushplus.template': 'Template', + 'page.settings.tab.config.push.form.pushplus.channel': 'Channel', + + 'page.settings.tab.config.push.form.action.ok': 'Ok', + 'page.settings.tab.config.push.form.action.cancel': 'Cancel', + 'page.settings.tab.config.push.form.action.test': 'Test' } diff --git a/webui/src/views/settings/components/config/locale/zh-CN.ts b/webui/src/views/settings/components/config/locale/zh-CN.ts index e2847b050c..6eef7f33b1 100644 --- a/webui/src/views/settings/components/config/locale/zh-CN.ts +++ b/webui/src/views/settings/components/config/locale/zh-CN.ts @@ -50,7 +50,7 @@ export default { 'page.settings.tab.config.btn.enableSubmit.modal.content3': '确定要开启提交吗?', 'page.settings.tab.config.btn.allowScript': '允许 BTN 服务器下发脚本', 'page.settings.tab.config.btn.allowScript.warning': - '警告,这意味着远程服务器可以在你的设备上执行任意代码,请谨慎开启', + '警告:这意味着远程服务器可以在你的设备上执行任意代码,请谨慎开启', 'page.settings.tab.config.btn.allowScript.tips': '打开此选项后将允许 PeerBanHelper 接收并执行来自 BTN 服务器的动态脚本,这有助于提高反吸血精确度和反吸血效果。', @@ -74,5 +74,43 @@ export default { 'page.settings.tab.config.performance.title': '性能', 'page.settings.tab.config.performance.useEcoQOS': '使用 Windows EcoQos API', 'page.settings.tab.config.performance.useEcoQOS.tips': - '启用 Windows 平台上的 {link}以节约能源消耗,程序运行速度将降低,定时任务可能推迟' + '启用 Windows 平台上的 {link}以节约能源消耗,程序运行速度将降低,定时任务可能推迟', + + 'page.settings.tab.config.push.title': '消息通知', + 'page.settings.tab.config.push.description': + '配置消息通知,当有新的事件发生时,将会通过消息通知的方式通知您', + 'page.settings.tab.config.push.add': '新增', + 'page.settings.tab.config.push.edit': '编辑', + 'page.settings.tab.config.push.deleteConfirm': '确定删除?', + 'page.settings.tab.config.push.form.title.new': '新增推送渠道', + 'page.settings.tab.config.push.form.title.edit': '编辑推送渠道', + 'page.settings.tab.config.push.form.name': '名称', + 'page.settings.tab.config.push.form.name.placeholder': '请输入唯一名称', + 'page.settings.tab.config.push.form.type': '类型', + 'page.settings.tab.config.push.form.type.smtp': '邮件', + 'page.settings.tab.config.push.form.type.pushplus': 'Push+', + 'page.settings.tab.config.push.form.type.serverchan': 'Server酱', + 'page.settings.tab.config.push.form.type.telegram': 'Telegram', + + 'page.settings.tab.config.push.form.stmp.host': '主机', + 'page.settings.tab.config.push.form.stmp.port': '端口号', + 'page.settings.tab.config.push.form.stmp.sender': '发件人', + 'page.settings.tab.config.push.form.stmp.senderName': '发件人名称', + 'page.settings.tab.config.push.form.stmp.auth': '启用认证', + 'page.settings.tab.config.push.form.stmp.authInfo': '认证信息', + 'page.settings.tab.config.push.form.stmp.username': '用户名', + 'page.settings.tab.config.push.form.stmp.password': '密码', + 'page.settings.tab.config.push.form.stmp.encryption': '加密方式', + 'page.settings.tab.config.push.form.stmp.receivers': '收件人', + 'page.settings.tab.config.push.form.stmp.receivers.placeholder': '输入一个按回车输入下一个', + 'page.settings.tab.config.push.form.stmp.sendPartial': '分片发送', + + 'page.settings.tab.config.push.form.pushplus.token': 'Token', + 'page.settings.tab.config.push.form.pushplus.topic': 'Topic', + 'page.settings.tab.config.push.form.pushplus.template': '模版', + 'page.settings.tab.config.push.form.pushplus.channel': 'Channel', + + 'page.settings.tab.config.push.form.action.ok': '确定', + 'page.settings.tab.config.push.form.action.cancel': '取消', + 'page.settings.tab.config.push.form.action.test': '测试' } diff --git a/webui/src/views/settings/components/labs/index.vue b/webui/src/views/settings/components/labs/index.vue new file mode 100644 index 0000000000..5d30c2547d --- /dev/null +++ b/webui/src/views/settings/components/labs/index.vue @@ -0,0 +1,69 @@ + + diff --git a/webui/src/views/settings/components/labs/locale/en-US.ts b/webui/src/views/settings/components/labs/locale/en-US.ts new file mode 100644 index 0000000000..e585228cce --- /dev/null +++ b/webui/src/views/settings/components/labs/locale/en-US.ts @@ -0,0 +1,9 @@ +export default { + 'page.settings.tab.labs': 'Labs', + 'page.settings.tab.labs.welcome': 'Welcome to Labs', + 'page.settings.tab.labs.welcome.content': + 'This is the Lab, where we will try some unstable new features. These features may have better or worse effects on the ban. Please turn them on or off according to your needs. If you have any suggestions or ideas, please submit an issue or PR on {github}.', + 'page.settings.tab.labs.list': 'Experiment List', + 'page.settings.tab.labs.action.enable': 'Enable', + 'page.settings.tab.labs.enabled': 'Enabled' +} diff --git a/webui/src/views/settings/components/labs/locale/zh-CN.ts b/webui/src/views/settings/components/labs/locale/zh-CN.ts new file mode 100644 index 0000000000..bff0a1a31f --- /dev/null +++ b/webui/src/views/settings/components/labs/locale/zh-CN.ts @@ -0,0 +1,9 @@ +export default { + 'page.settings.tab.labs': '实验室', + 'page.settings.tab.labs.welcome': '欢迎来到实验室', + 'page.settings.tab.labs.welcome.content': + '这里是实验室,我们会在这里尝试一些不够稳定的新功能,这些功能对于封禁效果可能会更好或者更差,请根据你的需求自行开关。如果您有任何建议或者想法,欢迎在 {github} 上提交 issue 或者 PR。', + 'page.settings.tab.labs.list': '实验列表', + 'page.settings.tab.labs.action.enable': '启用', + 'page.settings.tab.labs.enabled': '已启用' +} diff --git a/webui/src/views/settings/components/script/locale/en-US.ts b/webui/src/views/settings/components/script/locale/en-US.ts deleted file mode 100644 index 47117ab094..0000000000 --- a/webui/src/views/settings/components/script/locale/en-US.ts +++ /dev/null @@ -1,32 +0,0 @@ -export default { - 'page.settings.tab.script.disable': 'Disabled in settings', - 'page.settings.tab.script.description': - 'You can manage custom scripts here, which will be executed during the ban process. ', - 'page.settings.tab.script.add': 'Add script', - 'page.settings.tab.script.column.id': 'ID', - 'page.settings.tab.script.column.name': 'Name', - 'page.settings.tab.script.column.author': 'Author', - 'page.settings.tab.script.column.cacheable': 'Cacheable', - 'page.settings.tab.script.column.cacheable.tips': - 'Whether to participate in caching, one peer in one seed will only be executed once if the cache has not expired', - 'page.settings.tab.script.column.threadSafe': 'Thread Safe', - 'page.settings.tab.script.column.version': 'Version', - 'page.settings.tab.script.column.actions': 'Actions', - 'page.settings.tab.script.column.actions.edit': 'Edit', - 'page.settings.tab.script.column.actions.view': 'View', - 'page.settings.tab.script.column.actions.delete': 'Are you sure you want to delete this script?', - 'page.settings.tab.script.detail.title.view': 'View script: ', - 'page.settings.tab.script.detail.title.edit': 'Edit script: ', - 'page.settings.tab.script.detail.form.name': 'Script name', - 'page.settings.tab.script.detail.loading': 'Loading editor, this may take a while...', - 'page.settings.tab.script.detail.failed': 'Unable to load editor', - 'page.settings.tab.script.detail.failed.tips': - 'Please check your network connection or refresh the page to try again', - 'page.settings.tab.script.detail.action.ok': 'Ok', - 'page.settings.tab.script.detail.action.cancel': 'Cancel', - 'page.settings.tab.script.warning': 'Security Warning', - 'page.settings.tab.script.warning.description': - 'Scripts are powerful and can access system resources. Only add scripts from trusted sources as they run with system privileges.', - 'page.settings.tab.script.warning.confirm': 'I understand', - 'page.settings.tab.script.warning.cancel': 'Not now' -} diff --git a/webui/src/views/settings/components/script/locale/zh-CN.ts b/webui/src/views/settings/components/script/locale/zh-CN.ts deleted file mode 100644 index afbff60661..0000000000 --- a/webui/src/views/settings/components/script/locale/zh-CN.ts +++ /dev/null @@ -1,31 +0,0 @@ -export default { - 'page.settings.tab.script.disable': '功能在设置中被禁用', - 'page.settings.tab.script.description': - '你可以在这里管理自定义脚本,这些脚本将在封禁的过程中执行。', - 'page.settings.tab.script.add': '新增脚本', - 'page.settings.tab.script.column.id': 'ID', - 'page.settings.tab.script.column.name': '名称', - 'page.settings.tab.script.column.author': '作者', - 'page.settings.tab.script.column.cacheable': '可缓存', - 'page.settings.tab.script.column.cacheable.tips': - '是否参与缓存,缓存未过期的情况下同一种子下的同一 Peer 仅执行一次', - 'page.settings.tab.script.column.threadSafe': '线程安全', - 'page.settings.tab.script.column.version': '版本', - 'page.settings.tab.script.column.actions': '操作', - 'page.settings.tab.script.column.actions.edit': '编辑', - 'page.settings.tab.script.column.actions.view': '查看', - 'page.settings.tab.script.column.actions.delete': '确定删除这条脚本?', - 'page.settings.tab.script.detail.title.view': '查看脚本:', - 'page.settings.tab.script.detail.title.edit': '编辑脚本:', - 'page.settings.tab.script.detail.form.name': '脚本名称', - 'page.settings.tab.script.detail.loading': '正在加载编辑器,这可能需要一些时间...', - 'page.settings.tab.script.detail.failed': '无法加载编辑器', - 'page.settings.tab.script.detail.failed.tips': '请检查网络连接或者刷新页面重试', - 'page.settings.tab.script.detail.action.ok': '确定', - 'page.settings.tab.script.detail.action.cancel': '取消', - 'page.settings.tab.script.warning': '安全警告', - 'page.settings.tab.script.warning.description': - '请勿添加任何由陌生人发送给你的脚本!脚本拥有执行任意代码和程序的能力,这可能导致您的设备被黑客控制或攻击,添加任何脚本之前请三思而后行!', - 'page.settings.tab.script.warning.confirm': '我已了解上述风险', - 'page.settings.tab.script.warning.cancel': '不是现在' -} diff --git a/webui/src/views/settings/index.vue b/webui/src/views/settings/index.vue index cc6ec56d61..d2778ce46c 100644 --- a/webui/src/views/settings/index.vue +++ b/webui/src/views/settings/index.vue @@ -1,32 +1,35 @@ diff --git a/webui/src/views/settings/locale/en-US.ts b/webui/src/views/settings/locale/en-US.ts index 9ac4d93565..46bb63465a 100644 --- a/webui/src/views/settings/locale/en-US.ts +++ b/webui/src/views/settings/locale/en-US.ts @@ -1,13 +1,12 @@ import ConfigLocale from '../components/config/locale/en-US' import InfoLocale from '../components/info/locale/en-US' +import LabsLocalel from '../components/labs/locale/en-US' import ProfileLocale from '../components/profile/locale/en-US' -import ScriptLocal from '../components/script/locale/en-US' export default { 'page.settings.tab.config': 'Settings', 'page.settings.tab.profile': 'Profile', - 'page.settings.tab.script': 'Custom Script', ...ProfileLocale, ...ConfigLocale, ...InfoLocale, - ...ScriptLocal + ...LabsLocalel } diff --git a/webui/src/views/settings/locale/zh-CN.ts b/webui/src/views/settings/locale/zh-CN.ts index 505028d241..cb86eee840 100644 --- a/webui/src/views/settings/locale/zh-CN.ts +++ b/webui/src/views/settings/locale/zh-CN.ts @@ -1,13 +1,12 @@ import ConfigLocale from '../components/config/locale/zh-CN' import InfoLocale from '../components/info/locale/zh-CN' +import LabsLocalel from '../components/labs/locale/zh-CN' import ProfileLocale from '../components/profile/locale/zh-CN' -import ScriptLocal from '../components/script/locale/zh-CN' export default { 'page.settings.tab.config': '基础设置', 'page.settings.tab.profile': '首选项', - 'page.settings.tab.script': '自定义脚本', ...ProfileLocale, ...ConfigLocale, ...InfoLocale, - ...ScriptLocal + ...LabsLocalel }