diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 17d19dcba4..49fef9e88d 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,5 +1,5 @@
-name: '错误报告'
-description: '报告与 PeerBanHelper 有关的程序错误'
+name: '错误报告 - Bug Report'
+description: '报告与 PeerBanHelper 有关的程序错误 - Report the errors that related to PeerBanHelper'
title: '[BUG] '
labels:
@@ -8,42 +8,49 @@ body:
- type: 'markdown'
attributes:
value: |-
- ## 请注意
+ ## 请注意 - Caution
+ This form only used for bug report, for any other cases, please [click here](https://github.com/PBH-BTN/PeerBanHelper/issues/new)
此表单**仅用于反馈错误**,如果是其它类型的反馈,请[点击这里](https://github.com/PBH-BTN/PeerBanHelper/issues/new)。
-
+ If you think the error is related PBH WebUI, please [report to here](https://github.com/PBH-BTN/pbh-fe)
+ 如果你认为此错误是一个 PBH WebUI,请[在此反馈](https://github.com/PBH-BTN/pbh-fe)。
请尽可能完整且详细地填写所有表单项,以便我们以最高效率并准确的排查故障和诊断问题
- type: 'textarea'
attributes:
- label: '版本号'
+ label: '版本号 - Version'
description: |-
+ Enter the PBH version that display on WebUI footer or GUI window title.
输入您正在使用 PeerBanHelper 的版本号,通常可在窗口标题或者 WebUI 页面的底部找到
placeholder: 'vX.X.X'
validations:
required: true
- type: 'textarea'
attributes:
- label: '操作系统平台和系统架构'
+ label: '操作系统平台和系统架构 - OS and CPU Arch'
description: |-
+ Enter the OS version/arch that PBH running on (not downloader), E.g Windows, Debian, iStoreOS.
输入 PBH 所在的操作系统平台(不是下载器),例如:Windows、Debian、iStoreOS 等
+ And please also enter the system arch if you know it, E.g x86, arm64
此外,您还需要输入系统架构。如果是 x86 设备,则通常为 x64;如果是 arm 设备,则通常为 arm64。请根据实际情况填写。如果不知道,也可以不写系统架构类型。
placeholder: '操作系统平台名称……'
validations:
required: true
- type: 'textarea'
attributes:
- label: '部署方式'
+ label: '部署方式 - Deploy method'
description: |-
+ Enter the deploy method that you're using:
输入您部署 PeerBanHelper 方式,官方支持的有如下几种方式:
- * Windows 安装程序(通过 .exe 安装)
- * Windows 绿色懒人包(解压即用的 .zip 文件)
- * Docker 镜像
+ * Windows 安装程序(通过 .exe 安装) (Windows .EXE Installer)
+ * Windows 绿色懒人包(解压即用的 .zip 文件) (Windows .ZIP Portable)
+ * Docker 镜像 (Docker Container)
placeholder: '部署方式……'
validations:
required: true
- type: 'textarea'
attributes:
- label: '关联的下载器类型'
+ label: '关联的下载器类型 - Downloader Type'
description: |-
+ Enter the downloader type that you trying to connecting/connected to PBH (E.g):
输入您的 PBH 关联的下载器类型,例如:
* qBittorrent
* Transmission
@@ -54,15 +61,18 @@ body:
required: true
- type: 'textarea'
attributes:
- label: '问题描述'
+ label: '问题描述 - Issue Description'
description: |-
+ Describe the problem you encounted.
在此详细的描述你所遇到的问题
validations:
required: true
- type: 'textarea'
attributes:
- label: '复现步骤'
- description: '如果你清楚如何复现此故障,也欢迎告诉我们,帮助我们更快的复现它。如果它是一个偶尔才会出现的错误,请告诉我们它通常可能会在什么情况下出现。'
+ label: '复现步骤 - Reproduce steps'
+ description: |-
+ If you know how to reproduce the error, please type it in this text area.
+ 如果你清楚如何复现此故障,也欢迎告诉我们,帮助我们更快的复现它。如果它是一个偶尔才会出现的错误,请告诉我们它通常可能会在什么情况下出现。
placeholder: |-
1. 第一步
2. ...
@@ -71,31 +81,36 @@ body:
required: true
- type: 'textarea'
attributes:
- label: '截图/日志文件'
- description: '如果你有一些截图或者日志能够更好的解释你所提出的问题,你可以在这里上传。'
+ label: '截图/日志文件 - Screenshot / Logs '
+ description: |-
+ If you have some screenshot or logs file can help us, please upload them here.
+ 如果你有一些截图或者日志能够更好的解释你所提出的问题,你可以在这里上传。
placeholder: '<截图文件>'
validations:
required: false
- type: 'textarea'
attributes:
- label: '额外信息'
- description: '如果你还有其他觉得可能对排查和解决此问题有帮助的更多信息,可以在这里告诉我们'
+ label: '额外信息 - Addition Information'
+ description: |-
+ If you have any related informations, please insert them into this text area.
+ 如果你还有其他觉得可能对排查和解决此问题有帮助的更多信息,可以在这里告诉我们
placeholder: '在此填写可能有用的额外信息...'
- type: checkboxes
id: check-list
attributes:
- label: 检查清单
- description: 请检查并勾选下面的所有的复选框,如果您没有这样做,我们可能会直接关闭这个 Issue
+ label: 检查清单 - Check list
+ description: |-
+ Check and tick checkboxes that listed below
options:
- - label: "我确定正在运行 Github Releases 中的最新的正式版本 PeerBanHelper"
+ - label: "我确定正在运行 Github Releases 中的最新的正式版本 PeerBanHelper (I'm running the latest version of PBH that can be found in Github Relases)"
required: false
- - label: "我确定我所添加的下载器已满足 README 中的前置要求(如版本号和插件)"
+ - label: "我确定我所添加的下载器已满足 README 中的前置要求(如版本号和插件)(The downloaders that I've added already satisfied the requirements (E.g install plugins/adapters))"
required: false
- - label: "我确定我所提到的问题,均未在 README 和 WIKI 中有所解答"
+ - label: "我确定我所提到的问题,均未在 README 和 WIKI 中有所解答 (This not a question/or the question that not listed in README's FAQ or WIKI)"
required: false
- - label: "我确定我没有检查这个检查清单,只是闭眼选中了所有的复选框"
+ - label: "我确定我没有检查这个检查清单,只是闭眼选中了所有的复选框 (I have not read these checkboxes and therefore I just ticked them all)"
required: false
- - label: "我确定这不是一个与安全有关的安全漏洞,它可以被安全的公开报告"
+ - label: "我确定这不是一个与安全有关的安全漏洞,它可以被安全的公开报告 (This not a security related issue, can be safe report in public)"
required: false
- - label: "我确定我已知悉,如果我没有正确地填写问题报告表单,则 Issue 可能会被关闭"
+ - label: "我确定我已知悉,如果我没有正确地填写问题报告表单,则 Issue 可能会被关闭 (I know this issue may closed without any warnings if I didn't fill the form correctly)"
required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..0cbc137cbd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,7 @@
+blank_issues_enabled: true
+contact_links:
+ - name: 'WebUI 错误报告 - WebUI Bug Report'
+ about: |-
+ 报告与 PBH WebUI 有关的错误
+ Report the errors that related to PBH WebUI.
+ url: 'https://github.com/PBH-BTN/pbh-fe/issues/new'
diff --git a/.github/workflows/jvm-ci.yml b/.github/workflows/jvm-ci.yml
index 5e16718258..5f9ef06936 100644
--- a/.github/workflows/jvm-ci.yml
+++ b/.github/workflows/jvm-ci.yml
@@ -10,9 +10,9 @@ name: Java CI
on:
push:
- branches: [ "master", "release" ]
+ branches: [ "master", "major-refactor", "release" ]
pull_request:
- branches: [ "master", "release" ]
+ branches: [ "master", "major-refactor", "release" ]
workflow_dispatch:
release:
types:
@@ -31,8 +31,13 @@ jobs:
distribution: "temurin"
java-version: "21"
cache: "maven"
+ - uses: luangong/setup-install4j@v1
+ name: Setup Install4j
+ with:
+ version: 10.0.8
+ license: ${{ secrets.INSTALL4J_LICENSE }}
- name: Build with Maven
- run: mvn -B clean package --file pom.xml
+ run: mvn -B clean package --file pom.xml -P install4j-ci,thin-sqlite-packaging
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
@@ -40,6 +45,7 @@ jobs:
path: |
target/*.jar
target/peerbanhelper-binary
+ target/media/*.exe
id: project
- name: Set Up QEMU
uses: docker/setup-qemu-action@v3
@@ -65,7 +71,7 @@ jobs:
type=raw,ci
type=sha
- name: Build and push Docker image
- uses: docker/build-push-action@v5.3.0
+ uses: docker/build-push-action@v6.4.0
with:
context: .
file: ./Dockerfile-CI
@@ -75,3 +81,4 @@ jobs:
linux/arm64/v8
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}-jvm-universal
+
diff --git a/.github/workflows/jvm-release.yml b/.github/workflows/jvm-release.yml
index c49c40dbb9..8a69ae444e 100644
--- a/.github/workflows/jvm-release.yml
+++ b/.github/workflows/jvm-release.yml
@@ -15,6 +15,15 @@ on:
- published
jobs:
build:
+ permissions:
+ contents: write
+ checks: write
+ actions: read
+ issues: read
+ packages: write
+ pull-requests: read
+ repository-projects: read
+ statuses: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -27,8 +36,13 @@ jobs:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
+ - uses: luangong/setup-install4j@v1
+ name: Setup Install4j
+ with:
+ version: 10.0.8
+ license: ${{ secrets.INSTALL4J_LICENSE }}
- name: Build with Maven
- run: mvn -B clean package --file pom.xml
+ run: mvn -B clean package --file pom.xml -P install4j-ci,thin-sqlite-packaging
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
@@ -36,6 +50,13 @@ jobs:
path: |
target/*.jar
target/peerbanhelper-binary
+ target/media/*.exe
+ - name: Upload release binaries
+ uses: alexellis/upload-assets@0.4.1
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ with:
+ asset_paths: '["target/PeerBanHelper.jar", "target/media/PeerBanHelper_*"]'
id: project
- name: Set Up QEMU
uses: docker/setup-qemu-action@v3
@@ -61,7 +82,7 @@ jobs:
type=raw,latest
type=sha
- name: Build and push Docker image
- uses: docker/build-push-action@v5.3.0
+ uses: docker/build-push-action@v6.4.0
with:
context: .
file: ./Dockerfile
@@ -93,7 +114,7 @@ jobs:
type=raw,latest
type=sha
- name: Build and push Aliyun ACR
- uses: docker/build-push-action@v5.3.0
+ uses: docker/build-push-action@v6.4.0
with:
context: .
file: ./Dockerfile
@@ -102,4 +123,4 @@ jobs:
linux/amd64
linux/arm64/v8
tags: ${{ steps.meta-acr.outputs.tags }}
- labels: ${{ steps.meta-acr.outputs.labels }}-jvm-universal
\ No newline at end of file
+ labels: ${{ steps.meta-acr.outputs.labels }}-jvm-universal
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 3263bb5f22..6a62beb9c4 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -10,7 +10,7 @@ jobs:
issues: write
pull-requests: write
steps:
- - uses: actions/stale@v5
+ - uses: actions/stale@v9
with:
# issues
only-issue-labels: "waiting-reply"
diff --git a/Dockerfile b/Dockerfile
index d198b31238..7d24cd8058 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM --platform=$BUILDPLATFORM docker.io/maven:3.9.6-eclipse-temurin-21 as build
+FROM --platform=$BUILDPLATFORM docker.io/maven:3.9.8-eclipse-temurin-21 as build
COPY . /build
WORKDIR /build
@@ -12,4 +12,4 @@ WORKDIR /app
VOLUME /tmp
COPY --from=build build/target/PeerBanHelper.jar /app/PeerBanHelper.jar
ENV PATH "${JAVA_HOME}/bin:${PATH}"
-ENTRYPOINT ["java","-Xmx256M","-XX:+UseG1GC", "-XX:+UseStringDeduplication","-XX:+ShrinkHeapInSteps","-jar","PeerBanHelper.jar"]
\ No newline at end of file
+ENTRYPOINT ["java","-Xmx386M","-XX:+UseG1GC", "-XX:+UseStringDeduplication","-XX:+ShrinkHeapInSteps","-jar","PeerBanHelper.jar"]
\ No newline at end of file
diff --git a/Dockerfile-CI b/Dockerfile-CI
index 28f96f9e21..d08ff845a1 100644
--- a/Dockerfile-CI
+++ b/Dockerfile-CI
@@ -6,4 +6,4 @@ ENV TZ=UTC
WORKDIR /app
VOLUME /tmp
ENV PATH "${JAVA_HOME}/bin:${PATH}"
-ENTRYPOINT ["java","-Xmx256M","-XX:+UseG1GC", "-XX:+UseStringDeduplication","-XX:+ShrinkHeapInSteps","-jar","PeerBanHelper.jar"]
\ No newline at end of file
+ENTRYPOINT ["java","-Xmx386M","-XX:+UseG1GC", "-XX:+UseStringDeduplication","-XX:+ShrinkHeapInSteps","-jar","PeerBanHelper.jar"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 6693cfa2bb..249c077193 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@
* Transmission **(3.00-20 或更高版本)**
* BiglyBT(需要安装[插件](https://github.com/PBH-BTN/PBH-Adapter-BiglyBT))
* Deluge(需要安装[插件](https://github.com/PBH-BTN/PBH-Adapter-Deluge))
+* Azureus(Vuze)(需要安装[插件](https://github.com/PBH-BTN/PBH-Adapter-Azureus))
## 功能介绍
@@ -36,11 +37,14 @@ PeerBanHelper 主要由以下几个功能模块组成:
* Client Name 黑名单
* IP 黑名单
* 虚假进度检查器(提供启发式客户端检测功能)(Transmission不支持过量下载检测)
-* 主动探测
* 自动 IP 段封禁
* 多拨追猎
* Peer ID/Client Name 伪装检查
-* WebUI (目前支持:活跃封禁名单查看,历史封禁查询,封禁最频繁的 Top 50 IP)
+* WebUI (目前支持:活跃封禁名单查看,历史封禁查询,封禁最频繁的 Top 50 IP,规则订阅管理,图表查看,Peer 列表查看)
+
+如果配置了 Maxmind IP 库,则还支持以下内容:
+
+* 在封禁列表中查看 IP 归属地
### PeerID 黑名单
diff --git a/docker-compose.yml b/docker-compose.yml
index 33fe8507cb..193d45f678 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,4 +11,5 @@ services:
environment:
- PUID=0
- PGID=0
- - TZ=UTC
\ No newline at end of file
+ - TZ=UTC
+ stop_grace_period: 30s
\ No newline at end of file
diff --git a/install4j/icon.ico b/install4j/icon.ico
new file mode 100644
index 0000000000..7530b24998
Binary files /dev/null and b/install4j/icon.ico differ
diff --git a/install4j/icon.png b/install4j/icon.png
new file mode 100644
index 0000000000..b9033555c5
Binary files /dev/null and b/install4j/icon.png differ
diff --git a/install4j/lang/custom.utf8 b/install4j/lang/custom.utf8
new file mode 100644
index 0000000000..b23bd54813
--- /dev/null
+++ b/install4j/lang/custom.utf8
@@ -0,0 +1,6 @@
+launcher.peerbanhelper.gui=PeerBanHelper
+launcher.peerbanhelper.gui.swing=PeerBanHelper(兼容模式)
+launcher.peerbanhelper.nogui=PeerBanHelper(无GUI, 控制台)
+launcher.peerbanhelper.service=PeerBanHelper(服务)
+checkbox.followsystemstartup=登录时自动启动到系统托盘
+peerbanhelper.description=PeerBanHelper
\ No newline at end of file
diff --git a/install4j/lang/en-US.utf8 b/install4j/lang/en-US.utf8
new file mode 100644
index 0000000000..d9be99bcba
--- /dev/null
+++ b/install4j/lang/en-US.utf8
@@ -0,0 +1,6 @@
+launcher.peerbanhelper.gui=PeerBanHelper
+launcher.peerbanhelper.gui.swing=PeerBanHelper(Compatibility Mode)
+launcher.peerbanhelper.nogui=PeerBanHelper(NoGUI, Console)
+launcher.peerbanhelper.service=PeerBanHelper(Service)
+checkbox.followsystemstartup=Boot automatically to the system tray when logged in
+peerbanhelper.description=PeerBanHelper
\ No newline at end of file
diff --git a/install4j/lang/zh-CN.utf8 b/install4j/lang/zh-CN.utf8
new file mode 100644
index 0000000000..6206ed964d
--- /dev/null
+++ b/install4j/lang/zh-CN.utf8
@@ -0,0 +1,6 @@
+launcher.peerbanhelper.gui=PeerBanHelper
+launcher.peerbanhelper.gui.swing=PeerBanHelper(兼容模式)
+launcher.peerbanhelper.nogui=PeerBanHelper(无GUI, 控制台)
+launcher.peerbanhelper.service=PeerBanHelper(服务)
+checkbox.followsystemstartup=登录到桌面时自动启动
+peerbanhelper.description=一个能够自动封禁不受欢迎、吸血和异常的 Peers,并支持自定义规则的 BT 客户端辅助工具
\ No newline at end of file
diff --git a/install4j/project.install4j b/install4j/project.install4j
new file mode 100644
index 0000000000..3a0e174d2b
--- /dev/null
+++ b/install4j/project.install4j
@@ -0,0 +1,582 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sys.installationDir
+
+
+ context.getBooleanVariable("sys.confirmedUpdateInstallation")
+
+
+
+
+
+ ${form:welcomeMessage}
+
+ !context.isConsole()
+
+
+
+
+
+
+
+
+
+
+ updateCheck
+
+
+
+
+ ${i18n:ClickNext}
+
+
+
+
+
+ !context.getBooleanVariable("sys.confirmedUpdateInstallation")
+
+
+
+
+ sys.installationDir
+
+
+ context.getVariable("sys.responseFile") == null
+
+
+
+
+
+ ${i18n:SelectDirLabel(${compiler:sys.fullName})}
+
+
+
+
+
+
+
+ suggestAppDir
+ validateApplicationId
+ existingDirWarning
+ checkWritable
+ manualEntryAllowed
+ checkFreeSpace
+ showRequiredDiskSpace
+ showFreeDiskSpace
+ allowSpacesOnUnix
+ validationScript
+ standardValidation
+
+
+
+
+
+
+
+
+ ${i18n:SelectComponentsLabel2}
+
+ !context.isConsole()
+
+
+
+
+
+
+ selectionChangedScript
+
+
+
+
+
+
+
+
+ ${form:confirmationMessage}
+
+ !context.isConsole()
+
+
+
+ ${i18n:CreateDesktopIcon}
+ createDesktopLinkAction
+
+
+
+
+ ${i18n:checkbox.followsystemstartup}
+ startupWhenLoggedIn
+
+
+
+
+
+
+
+
+
+ ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}
+
+ !context.getBooleanVariable("sys.programGroupDisabled")
+
+
+
+
+
+
+
+ ${compiler:sys.fullName}
+
+
+
+
+
+
+
+
+
+
+ context.getBooleanVariable("createDesktopLinkAction")
+
+
+
+
+
+
+ PeerBanHelper
+
+ context.getBooleanVariable("startupWhenLoggedIn")
+
+
+
+ ${compiler:sys.fullName} ${compiler:sys.version}
+
+
+
+
+
+
+ ${i18n:WizardPreparing}
+
+
+
+
+
+
+
+
+ ${form:finishedMessage}
+
+
+
+
+
+
+
+
+ ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${form:welcomeMessage}
+
+ !context.isConsole()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${i18n:UninstallerPreparing}
+
+
+
+
+
+
+
+
+
+ ${form:successMessage}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 5fcc768899..d8eb2b65e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.ghostchu.peerbanhelper
peerbanhelper
- 4.4.0
+ 5.0.0
takari-jar
PeerBanHelper
@@ -17,20 +17,24 @@
com.ghostchu.peerbanhelper.MainJumpLoader
yyyyMMdd-HHmmss
3.4.1
- 5.8.27
22.0.1
provided
- 126.2.0
- 126.2.0
-
+ /opt/install4j
+ 6.1
+
+
+ ej-technologies
+ https://maven.ej-technologies.com/repository
+
+
org.apache.maven.plugins
maven-compiler-plugin
- 3.12.1
+ 3.13.0
true
true
@@ -41,7 +45,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.5.1
+ 3.6.0
org.apache.logging.log4j
@@ -78,17 +82,18 @@
+ *:*
- META-INF/*.SF
- META-INF/*.DSA
- META-INF/*.RSA
- META-INF/*.kotlin_module
- META-INF/*.txt
- META-INF/proguard/*
- META-INF/services/*
- META-INF/versions/9/*
- *License*
- *LICENSE*
+
+
+
+
+
+
+
+
+
+
@@ -103,7 +108,7 @@
io.github.git-commit-id
git-commit-id-maven-plugin
- 5.0.0
+ 5.0.1
get-the-git-infos
@@ -137,7 +142,7 @@
io.takari.maven.plugins
takari-lifecycle-plugin
- 2.1.5
+ 2.1.6
true
proc
@@ -155,7 +160,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.5.3
+ 3.6.0
@@ -187,7 +192,7 @@
io.github.git-commit-id
git-commit-id-maven-plugin
- 5.0.0
+ 5.0.1
get-the-git-infos
@@ -220,6 +225,21 @@
io.takari.maven.plugins
takari-lifecycle-plugin
+
+ maven-dependency-plugin
+ 3.7.1
+
+
+ generate-sources
+
+ build-classpath
+
+
+ pbh.classpath
+
+
+
+
@@ -233,6 +253,9 @@
src/main/resources/static
+
+ .git/
+
static
false
@@ -306,6 +329,114 @@
compile
+
+ install4j-ci
+
+
+
+ com.install4j
+ install4j-maven
+ 10.0.8
+
+
+ compile-installers
+ package
+
+ compile
+
+
+ ${install4j.home}
+ ${project.basedir}/install4j/project.install4j
+
+ ${project.basedir}/target/PeerBanHelper.jar
+
+
+
+
+
+
+
+
+
+ install4j-dev
+
+
+
+ com.install4j
+ install4j-maven
+ 10.0.8
+
+
+ compile-installers
+ package
+
+ compile
+
+
+ ${install4j.home}
+ ${project.basedir}/install4j/project.install4j
+
+ ${project.basedir}/target/PeerBanHelper.jar
+
+
+
+
+
+
+
+
+ C:\Program Files\install4j10
+
+
+
+
+ thin-sqlite-packaging
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+ org.apache.logging.log4j
+ log4j-transform-maven-shade-plugin-extensions
+ 0.1.0
+
+
+
+
+ package
+
+ shade
+
+
+
+
+ *:*
+
+ org/sqlite/native/Linux-Android/**
+ org/sqlite/native/Linux/x86/**
+ org/sqlite/native/Linux/arm/**
+ org/sqlite/native/Linux/armv7/**
+ org/sqlite/native/Linux/armv6/**
+ org/sqlite/native/Windows/armv7/**
+ org/sqlite/native/Windows/x86/**
+ org/sqlite/native/Linux-Musl/x86/**
+ org/sqlite/native/FreeBSD/x86/**
+
+
+
+
+
+
+
+
+
+
@@ -337,25 +468,25 @@
org.bspfsystems
yamlconfiguration
- 2.0.1
+ 2.0.2
compile
org.projectlombok
lombok
- 1.18.30
+ 1.18.34
provided
com.google.code.gson
gson
- 2.10.1
+ 2.11.0
com.google.guava
guava
- 33.0.0-jre
+ 33.2.1-jre
org.jetbrains
@@ -367,17 +498,17 @@
com.github.seancfoley
ipaddress
- 5.4.2
+ 5.5.0
org.slf4j
slf4j-api
- 2.0.12
+ 2.0.13
org.xerial
sqlite-jdbc
- 3.45.2.0
+ 3.46.0.0
com.zaxxer
@@ -408,7 +539,7 @@
org.junit.jupiter
junit-jupiter-api
- 5.10.2
+ 5.10.3
test
@@ -425,7 +556,7 @@
io.javalin
javalin
- 6.1.3
+ 6.1.6
@@ -470,6 +601,11 @@
${javafx.version}
${javafx.scope}
+
+
+
+
+
com.googlecode.aviator
aviator
@@ -491,5 +627,53 @@
json
20240303
+
+
+ org.springframework
+ spring-context
+ 6.1.11
+
+
+
+ com.j256.ormlite
+ ormlite-core
+ ${ormlite.version}
+
+
+
+ com.j256.ormlite
+ ormlite-jdbc
+ ${ormlite.version}
+
+
+ fr.turri
+ aXMLRPC
+ 1.14.0
+
+
+ com.ghostchu
+ simplereloadlib
+ 1.1.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000000..5db72dd6a9
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended"
+ ]
+}
diff --git a/src/main/java/com/ghostchu/peerbanhelper/AppConfig.java b/src/main/java/com/ghostchu/peerbanhelper/AppConfig.java
new file mode 100644
index 0000000000..ff7f4b471e
--- /dev/null
+++ b/src/main/java/com/ghostchu/peerbanhelper/AppConfig.java
@@ -0,0 +1,33 @@
+package com.ghostchu.peerbanhelper;
+
+import com.ghostchu.simplereloadlib.ReloadManager;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import java.io.File;
+
+@Configuration
+@ComponentScan("com.ghostchu.peerbanhelper")
+@Slf4j
+public class AppConfig {
+ @Bean
+ public BuildMeta buildMeta() {
+ return Main.getMeta();
+ }
+
+ @Bean("banListFile")
+ public File banListFile() {
+ return new File(Main.getDataDirectory(), "banlist.dump");
+ }
+
+ @Bean("userAgent")
+ public String userAgent() {
+ return Main.getUserAgent();
+ }
+
+ public ReloadManager reloadManager() {
+ return Main.getReloadManager();
+ }
+}
diff --git a/src/main/java/com/ghostchu/peerbanhelper/BuildMeta.java b/src/main/java/com/ghostchu/peerbanhelper/BuildMeta.java
index fae8902535..00bd5df0d4 100644
--- a/src/main/java/com/ghostchu/peerbanhelper/BuildMeta.java
+++ b/src/main/java/com/ghostchu/peerbanhelper/BuildMeta.java
@@ -8,7 +8,7 @@
@Data
@NoArgsConstructor
@AllArgsConstructor
-public class BuildMeta {
+public final class BuildMeta {
private String version = "unknown";
private String os;
private String branch;
diff --git a/src/main/java/com/ghostchu/peerbanhelper/Main.java b/src/main/java/com/ghostchu/peerbanhelper/Main.java
index a49c4b14b2..8e60b484e9 100644
--- a/src/main/java/com/ghostchu/peerbanhelper/Main.java
+++ b/src/main/java/com/ghostchu/peerbanhelper/Main.java
@@ -10,9 +10,9 @@
import com.ghostchu.peerbanhelper.gui.impl.console.ConsoleGuiImpl;
import com.ghostchu.peerbanhelper.gui.impl.javafx.JavaFxImpl;
import com.ghostchu.peerbanhelper.gui.impl.swing.SwingGuiImpl;
-import com.ghostchu.peerbanhelper.text.Lang;
import com.ghostchu.peerbanhelper.util.PBHLibrariesLoader;
import com.ghostchu.peerbanhelper.util.Slf4jLogAppender;
+import com.ghostchu.simplereloadlib.ReloadManager;
import com.google.common.eventbus.EventBus;
import com.google.common.io.ByteStreams;
import lombok.Getter;
@@ -20,6 +20,11 @@
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
import org.bspfsystems.yamlconfiguration.configuration.InvalidConfigurationException;
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.awt.*;
import java.io.File;
@@ -29,11 +34,17 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
@Slf4j
public class Main {
+ @Getter
+ private static final EventBus eventBus = new EventBus();
+ @Getter
+ private static final ReloadManager reloadManager = new ReloadManager();
+ public static String DEF_LOCALE = Locale.getDefault().toLanguageTag();
@Getter
private static File dataDirectory;
@Getter
@@ -43,14 +54,10 @@ public class Main {
private static File pluginDirectory;
private static File libraryDirectory;
@Getter
- private static BuildMeta meta = new BuildMeta();
- @Getter
private static PeerBanHelperServer server;
@Getter
private static PBHGuiManager guiManager;
@Getter
- private static final EventBus eventBus = new EventBus();
- @Getter
private static File mainConfigFile;
@Getter
private static File profileConfigFile;
@@ -58,34 +65,60 @@ public class Main {
private static LibraryManager libraryManager;
@Getter
private static PBHLibrariesLoader librariesLoader;
+ @Getter
+ private static AnnotationConfigApplicationContext applicationContext;
+ @Getter
+ private static String pbhServerAddress;
+ @Getter
+ private static YamlConfiguration mainConfig;
+ @Getter
+ private static YamlConfiguration profileConfig;
+ @Getter
+ private static BuildMeta meta;
+ @Getter
+ private static String[] startupArgs;
public static void main(String[] args) {
+ startupArgs = args;
setupConfDirectory(args);
setupLog4j2();
- setupProxySettings();
+ Path librariesPath = dataDirectory.toPath().toAbsolutePath().resolve("libraries");
libraryManager = new PBHLibraryManager(
new Slf4jLogAppender(),
- dataDirectory.toPath(), "libraries"
+ Main.getDataDirectory().toPath(), "libraries"
);
- Path librariesPath = dataDirectory.toPath().toAbsolutePath().resolve("libraries");
libraryManager.setLogLevel(LogLevel.ERROR);
librariesLoader = new PBHLibrariesLoader(libraryManager, librariesPath);
- initBuildMeta();
- initGUI(args);
+ meta = buildMeta();
setupConfiguration();
- guiManager.createMainWindow();
-
mainConfigFile = new File(configDirectory, "config.yml");
- YamlConfiguration mainConfig = loadConfiguration(mainConfigFile);
- new PBHConfigUpdater(mainConfigFile, mainConfig).update(new MainConfigUpdateScript(mainConfig));
+ mainConfig = loadConfiguration(mainConfigFile);
+ new PBHConfigUpdater(mainConfigFile, mainConfig, Main.class.getResourceAsStream("/config.yml")).update(new MainConfigUpdateScript(mainConfig));
profileConfigFile = new File(configDirectory, "profile.yml");
- YamlConfiguration profileConfig = loadConfiguration(profileConfigFile);
- new PBHConfigUpdater(profileConfigFile, profileConfig).update(new ProfileUpdateScript(profileConfig));
- String pbhServerAddress = mainConfig.getString("server.prefix", "http://127.0.0.1:" + mainConfig.getInt("server.http"));
+ profileConfig = loadConfiguration(profileConfigFile);
+ new PBHConfigUpdater(profileConfigFile, profileConfig, Main.class.getResourceAsStream("/profile.yml")).update(new ProfileUpdateScript(profileConfig));
+ log.info("Current system language tag: {}", Locale.getDefault().toLanguageTag());
+ DEF_LOCALE = mainConfig.getString("language");
+ if (DEF_LOCALE == null || DEF_LOCALE.equalsIgnoreCase("default")) {
+ DEF_LOCALE = Locale.getDefault().toLanguageTag();
+ }
+ DEF_LOCALE = DEF_LOCALE.toLowerCase(Locale.ROOT).replace("-", "_");
+ initGUI(args);
+ guiManager.createMainWindow();
+ pbhServerAddress = mainConfig.getString("server.prefix", "http://127.0.0.1:" + mainConfig.getInt("server.http"));
+ setupProxySettings();
try {
- server = new PeerBanHelperServer(pbhServerAddress, profileConfig, mainConfig);
+ applicationContext = new AnnotationConfigApplicationContext();
+ applicationContext.register(AppConfig.class);
+ applicationContext.refresh();
+ registerBean(File.class, mainConfigFile, "mainConfigFile");
+ registerBean(File.class, profileConfigFile, "profileConfigFile");
+ registerBean(YamlConfiguration.class, mainConfig, "mainConfig");
+ registerBean(YamlConfiguration.class, profileConfig, "profileConfig");
+ server = applicationContext.getBean(PeerBanHelperServer.class);
+ server.start();
} catch (Exception e) {
- log.error(Lang.BOOTSTRAP_FAILED, e);
+ log.error("Failed to startup PeerBanHelper, FATAL ERROR", e);
throw new RuntimeException(e);
}
guiManager.onPBHFullyStarted(server);
@@ -94,8 +127,23 @@ public static void main(String[] args) {
}
private static void setupProxySettings() {
- if (System.getenv("http_proxy") != null || System.getenv("HTTP_PROXY") != null) {
- log.warn(Lang.ALERT_INCORRECT_PROXY_SETTING);
+ var proxySection = mainConfig.getConfigurationSection("proxy");
+ if (proxySection == null) return;
+ String host = proxySection.getString("host");
+ String port = String.valueOf(proxySection.getInt("port"));
+ switch (proxySection.getInt("setting")) {
+ case 1 -> System.setProperty("java.net.useSystemProxies", "true");
+ case 2 -> {
+ System.setProperty("http.proxyHost", host);
+ System.setProperty("http.proxyPort", port);
+ System.setProperty("https.proxyHost", host);
+ System.setProperty("https.proxyPort", port);
+ }
+ case 3 -> {
+ System.setProperty("socksProxyHost", host);
+ System.setProperty("socksProxyPort", port);
+ }
+ default -> System.setProperty("java.net.useSystemProxies", "false");
}
}
@@ -106,7 +154,7 @@ private static void setupConfDirectory(String[] args) {
if (osName.contains("Windows")) {
root = new File(System.getenv("LOCALAPPDATA"), "PeerBanHelper").getAbsolutePath();
} else {
- root = new File(System.getProperty("user.home"), ".config/PeerBanHelper").getAbsolutePath();
+ root = new File(new File(System.getProperty("user.home"), ".config"), "PeerBanHelper").getAbsolutePath();
}
}
if (System.getProperty("pbh.datadir") != null) {
@@ -131,30 +179,46 @@ private static YamlConfiguration loadConfiguration(File file) {
try {
configuration.load(file);
} catch (IOException | InvalidConfigurationException e) {
- log.error(Lang.CONFIGURATION_INVALID, file);
- guiManager.createDialog(Level.SEVERE, Lang.CONFIGURATION_INVALID_TITLE, String.format(Lang.CONFIGURATION_INVALID_DESCRIPTION, file));
+ log.error("Unable to load configuration: invalid YAML configuration // 无法加载配置文件:无效的 YAML 配置,请检查是否有语法错误", e);
+ guiManager.createDialog(Level.SEVERE, "Invalid YAML configuration | 无效 YAML 配置文件", String.format("Failed to read configuration: %s", file));
System.exit(1);
}
return configuration;
}
private static void setupConfiguration() {
- log.info(Lang.LOADING_CONFIG);
+ log.info("Loading configuration...");
try {
- if (!initConfiguration()) {
- guiManager.showConfigurationSetupDialog();
- System.exit(0);
- }
+ initConfiguration();
+ //guiManager.showConfigurationSetupDialog();
+ //System.exit(0);
} catch (IOException e) {
- log.error(Lang.ERR_SETUP_CONFIGURATION, e);
+ log.error("Unable to load configuration, something went wrong!", e);
System.exit(0);
}
}
+ private static BuildMeta buildMeta() {
+ var meta = new BuildMeta();
+ try (InputStream stream = Main.class.getResourceAsStream("/build-info.yml")) {
+ if (stream == null) {
+ log.error("Error: Unable to load build metadata from JAR/build-info.yml: Bundled resources not exists");
+ } else {
+ String str = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
+ YamlConfiguration configuration = new YamlConfiguration();
+ configuration.loadFromString(str);
+ meta.loadBuildMeta(configuration);
+ }
+ } catch (IOException | InvalidConfigurationException e) {
+ log.error("Error: Unable to load build metadata from JAR/build-info.yml", e);
+ }
+ return meta;
+ }
+
private static void setupShutdownHook() {
Thread shutdownThread = new Thread(() -> {
try {
- log.info(Lang.PBH_SHUTTING_DOWN);
+ log.info("Shutting down...");
eventBus.post(new PBHShutdownEvent());
server.shutdown();
guiManager.close();
@@ -180,7 +244,7 @@ private static void initGUI(String[] args) {
guiType = "swing";
}
} catch (IOException e) {
- log.warn("Failed to load JavaFx dependencies", e);
+ log.error("Failed to load JavaFx dependencies", e);
guiType = "swing";
}
}
@@ -192,6 +256,10 @@ private static void initGUI(String[] args) {
guiManager.setup();
}
+ public static String getUserAgent() {
+ return "PeerBanHelper/" + meta.getVersion() + " BTN-Protocol/0.0.0-dev";
+ }
+
private static boolean loadJavaFxDependencies() throws IOException {
try (var is = Main.class.getResourceAsStream("/libraries/javafx.maven")) {
String str = new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8);
@@ -204,32 +272,17 @@ private static boolean loadJavaFxDependencies() throws IOException {
sysArch = "mac";
}
try {
- librariesLoader.loadLibraries(Arrays.stream(libraries).toList(), Map.of("system.platform", sysArch, "javafx.version", meta.getJavafx()));
+ librariesLoader.loadLibraries(Arrays.stream(libraries).toList(),
+ Map.of("system.platform", sysArch, "javafx.version",
+ Main.getMeta().getJavafx()));
return true;
} catch (Exception e) {
- log.warn("Unable to load JavaFx dependencies", e);
+ log.error("Unable to load JavaFx dependencies", e);
return false;
}
}
-
}
- private static void initBuildMeta() {
- meta = new BuildMeta();
- try (InputStream stream = Main.class.getResourceAsStream("/build-info.yml")) {
- if (stream == null) {
- log.error(Lang.ERR_BUILD_NO_INFO_FILE);
- } else {
- String str = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
- YamlConfiguration configuration = new YamlConfiguration();
- configuration.loadFromString(str);
- meta.loadBuildMeta(configuration);
- }
- } catch (IOException | InvalidConfigurationException e) {
- log.error(Lang.ERR_CANNOT_LOAD_BUILD_INFO, e);
- }
- log.info(Lang.MOTD, meta.getVersion());
- }
private static void handleCommand(String input) {
@@ -244,7 +297,7 @@ private static boolean initConfiguration() throws IOException {
configDirectory.mkdirs();
}
if (!configDirectory.isDirectory()) {
- throw new IllegalStateException(Lang.ERR_CONFIG_DIRECTORY_INCORRECT);
+ throw new IllegalStateException("The path " + configDirectory.getAbsolutePath() + " should be a directory but found a file.");
}
if (!pluginDirectory.exists()) {
pluginDirectory.mkdirs();
@@ -263,9 +316,59 @@ private static boolean initConfiguration() throws IOException {
return exists;
}
+ public static String decapitalize(String name) {
+ if (name == null || name.isEmpty()) {
+ return name;
+ }
+ if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
+ Character.isUpperCase(name.charAt(0))) {
+ return name;
+ }
+ char chars[] = name.toCharArray();
+ chars[0] = Character.toLowerCase(chars[0]);
+ return new String(chars);
+ }
+
+ public static void registerBean(Class clazz, @Nullable String beanName) {
+ if (beanName == null) {
+ beanName = decapitalize(clazz.getSimpleName());
+ }
+ if (applicationContext.containsBean(beanName)) {
+ return;
+ } else {
+ String bn = decapitalize(clazz.getSimpleName());
+ if (applicationContext.containsBean(bn)) {
+ return;
+ }
+ }
+ ConfigurableApplicationContext configurableApplicationContext = applicationContext;
+ DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
+ BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
+ defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
+ }
+
+ public static void registerBean(Class clazz, T instance, @Nullable String beanName) {
+ if (beanName == null) {
+ beanName = decapitalize(clazz.getSimpleName());
+ }
+ if (applicationContext.containsBean(beanName)) {
+ return;
+ } else {
+ String bn = decapitalize(clazz.getSimpleName());
+ if (applicationContext.containsBean(bn)) {
+ return;
+ }
+ }
+ DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
+ BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> instance);
+ defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
+ }
+
+ public static void unregisterBean(String beanName) {
+ ConfigurableApplicationContext configurableApplicationContext = applicationContext;
+ DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
+ defaultListableBeanFactory.removeBeanDefinition(beanName);
- public static String getUserAgent() {
- return "PeerBanHelper/" + meta.getVersion() + " BTN-Protocol/0.0.0-dev";
}
}
\ No newline at end of file
diff --git a/src/main/java/com/ghostchu/peerbanhelper/MainJavaFx.java b/src/main/java/com/ghostchu/peerbanhelper/MainJavaFx.java
index 24162770e1..e1d86f767e 100644
--- a/src/main/java/com/ghostchu/peerbanhelper/MainJavaFx.java
+++ b/src/main/java/com/ghostchu/peerbanhelper/MainJavaFx.java
@@ -1,7 +1,6 @@
package com.ghostchu.peerbanhelper;
import com.ghostchu.peerbanhelper.gui.impl.javafx.mainwindow.JFXWindowController;
-import com.ghostchu.peerbanhelper.text.Lang;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
@@ -36,7 +35,7 @@ public void start(Stage st) throws Exception {
stage = st;
FXMLLoader fxmlLoader = new FXMLLoader(MainJavaFx.class.getResource("/javafx/main_window.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 600, 400);
- st.setTitle(String.format(Lang.GUI_TITLE_LOADING, "JavaFx"));
+ st.setTitle(String.format("PeerBanHelper (JavaFx) - Loading..."));
st.setScene(scene);
st.setWidth(1000);
st.setHeight(600);
diff --git a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java
index 2dad0134b2..7568b7dcb2 100644
--- a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java
+++ b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java
@@ -1,9 +1,9 @@
package com.ghostchu.peerbanhelper;
import com.ghostchu.peerbanhelper.alert.AlertManager;
-import com.ghostchu.peerbanhelper.btn.BtnNetwork;
+import com.ghostchu.peerbanhelper.database.Database;
import com.ghostchu.peerbanhelper.database.DatabaseHelper;
-import com.ghostchu.peerbanhelper.database.DatabaseManager;
+import com.ghostchu.peerbanhelper.database.dao.impl.BanListDao;
import com.ghostchu.peerbanhelper.downloader.Downloader;
import com.ghostchu.peerbanhelper.downloader.DownloaderLastStatus;
import com.ghostchu.peerbanhelper.downloader.impl.biglybt.BiglyBT;
@@ -18,14 +18,15 @@
import com.ghostchu.peerbanhelper.invoker.impl.CommandExec;
import com.ghostchu.peerbanhelper.invoker.impl.IPFilterInvoker;
import com.ghostchu.peerbanhelper.ipdb.IPDB;
+import com.ghostchu.peerbanhelper.ipdb.IPGeoData;
import com.ghostchu.peerbanhelper.metric.BasicMetrics;
import com.ghostchu.peerbanhelper.metric.HitRateMetric;
-import com.ghostchu.peerbanhelper.metric.impl.persist.PersistMetrics;
import com.ghostchu.peerbanhelper.module.*;
import com.ghostchu.peerbanhelper.module.impl.rule.*;
import com.ghostchu.peerbanhelper.module.impl.webapi.*;
import com.ghostchu.peerbanhelper.peer.Peer;
import com.ghostchu.peerbanhelper.text.Lang;
+import com.ghostchu.peerbanhelper.text.TranslationComponent;
import com.ghostchu.peerbanhelper.torrent.Torrent;
import com.ghostchu.peerbanhelper.util.*;
import com.ghostchu.peerbanhelper.util.rule.ModuleMatchCache;
@@ -38,13 +39,7 @@
import com.ghostchu.peerbanhelper.wrapper.TorrentWrapper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonObject;
-import com.google.gson.reflect.TypeToken;
-import com.maxmind.geoip2.exception.GeoIp2Exception;
-import com.maxmind.geoip2.model.AsnResponse;
-import com.maxmind.geoip2.model.CityResponse;
import inet.ipaddr.IPAddress;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -53,112 +48,128 @@
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
+import static com.ghostchu.peerbanhelper.Main.DEF_LOCALE;
+import static com.ghostchu.peerbanhelper.text.TextManager.tl;
+import static com.ghostchu.peerbanhelper.text.TextManager.tlUI;
+
@Slf4j
+@Component
public class PeerBanHelperServer {
+ private static final long BANLIST_SAVE_INTERVAL = 60 * 60 * 1000;
+ private final CheckResult NO_MATCHES_CHECK_RESULT = new CheckResult(getClass(), PeerAction.NO_ACTION, 0, new TranslationComponent("No Matches"), new TranslationComponent("No Matches"));
private final Map BAN_LIST = new ConcurrentHashMap<>();
- private final YamlConfiguration profile;
- private final List downloaders = new ArrayList<>();
- @Getter
- private final long banDuration;
- @Getter
- private final int httpdPort;
- @Getter
- private final boolean hideFinishLogs;
+ private final Deque scheduledBanListOperations = new ConcurrentLinkedDeque<>();
+ private final List downloaders = new CopyOnWriteArrayList<>();
@Getter
private final List ignoreAddresses = new ArrayList<>();
- @Getter
- private final YamlConfiguration mainConfig;
- private final ModuleMatchCache moduleMatchCache;
- private final File banListFile;
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
@Getter
- private final HitRateMetric hitRateMetric = new HitRateMetric();
- @Getter
private final List banListInvoker = new ArrayList<>();
private final String pbhServerAddress;
- private ScheduledExecutorService BAN_WAVE_SERVICE;
+ private final ScheduledExecutorService GENERAL_SCHEDULER = Executors.newScheduledThreadPool(8, Thread.ofVirtual().factory());
@Getter
- private ImmutableMap LIVE_PEERS = ImmutableMap.of();
+ private YamlConfiguration profileConfig;
@Getter
- private BtnNetwork btnNetwork;
+ private long banDuration;
@Getter
- private BasicMetrics metrics;
- private DatabaseManager databaseManager;
+ private int httpdPort;
@Getter
- private DatabaseHelper databaseHelper;
+ private boolean hideFinishLogs;
+ @Getter
+ private YamlConfiguration mainConfig;
+ @Autowired
+ private ModuleMatchCache moduleMatchCache;
+ @Autowired
+ @Qualifier("banListFile")
+ private File banListFile;
+ private ScheduledExecutorService BAN_WAVE_SERVICE;
@Getter
+ private Map LIVE_PEERS = new HashMap<>();
+ @Autowired
+ @Qualifier("persistMetrics")
+ private BasicMetrics metrics;
+ @Autowired
+ private Database databaseManager;
+ @Autowired
private ModuleManager moduleManager;
@Getter
@Nullable
private IPDB ipdb = null;
private WatchDog banWaveWatchDog;
- @Getter
+ @Autowired
private JavalinWebContainer webContainer;
- @Getter
+ @Autowired
private AlertManager alertManager;
private Cache geoIpCache = CacheBuilder.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.maximumSize(3000)
.softValues()
.build();
-
-
- public PeerBanHelperServer(String pbhServerAddress, YamlConfiguration profile, YamlConfiguration mainConfig) throws SQLException {
- this.pbhServerAddress = pbhServerAddress;
- this.profile = profile;
- this.banDuration = profile.getLong("ban-duration");
- this.mainConfig = mainConfig;
+ @Getter
+ private HitRateMetric hitRateMetric = new HitRateMetric();
+ @Autowired
+ private DatabaseHelper databaseHelper;
+ @Autowired
+ private BanListDao banListDao;
+
+ public PeerBanHelperServer() {
+ this.pbhServerAddress = Main.getPbhServerAddress();
+ this.profileConfig = Main.getProfileConfig();
+ this.banDuration = profileConfig.getLong("ban-duration");
+ this.mainConfig = Main.getMainConfig();
this.httpdPort = mainConfig.getInt("server.http");
this.hideFinishLogs = mainConfig.getBoolean("logger.hide-finish-log");
- profile.getStringList("ignore-peers-from-addresses").forEach(ip -> {
+ profileConfig.getStringList("ignore-peers-from-addresses").forEach(ip -> {
IPAddress ignored = IPAddressUtil.getIPAddress(ip);
ignoreAddresses.add(ignored);
});
- this.banListFile = new File(Main.getDataDirectory(), "banlist.dump");
+ }
+
+ public void start() throws SQLException {
loadDownloaders();
- this.moduleMatchCache = new ModuleMatchCache();
+ registerBanListInvokers();
+ registerModules();
registerHttpServer();
- this.moduleManager = new ModuleManager();
setupIPDB();
- setupBtn();
- try {
- prepareDatabase();
- } catch (Exception e) {
- log.error(Lang.DATABASE_FAILURE, e);
- throw e;
- }
- registerMetrics();
- registerModules();
resetKnownDownloaders();
- registerBanListInvokers();
loadBanListToMemory();
registerTimer();
banListInvoker.forEach(BanListInvoker::reset);
+ GENERAL_SCHEDULER.scheduleWithFixedDelay(this::saveBanList, 10 * 1000, BANLIST_SAVE_INTERVAL, TimeUnit.MILLISECONDS);
Main.getEventBus().post(new PBHServerStartedEvent(this));
+ if (downloaders.isEmpty()) {
+ for (int i = 0; i < 50; i++) {
+ log.error(tlUI(Lang.NEW_SETUP_NO_DOWNLOADERS, getWebContainer() == null ? "ERROR" : getWebContainer().getToken()));
+ }
+ }
}
public void loadDownloaders() {
this.downloaders.clear();
ConfigurationSection clientSection = mainConfig.getConfigurationSection("client");
+ if (clientSection == null) {
+ return;
+ }
for (String client : clientSection.getKeys(false)) {
ConfigurationSection downloaderSection = clientSection.getConfigurationSection(client);
String endpoint = downloaderSection.getString("endpoint");
Downloader downloader = createDownloader(client, downloaderSection);
registerDownloader(downloader);
- log.info(Lang.DISCOVER_NEW_CLIENT, downloader.getType(), client, endpoint);
+ log.info(tlUI(Lang.DISCOVER_NEW_CLIENT, downloader.getType(), client, endpoint));
}
}
@@ -170,6 +181,7 @@ public Downloader createDownloader(String client, ConfigurationSection downloade
downloader = Transmission.loadFromConfig(client, pbhServerAddress, downloaderSection);
case "biglybt" -> downloader = BiglyBT.loadFromConfig(client, downloaderSection);
case "deluge" -> downloader = Deluge.loadFromConfig(client, downloaderSection);
+ //case "rtorrent" -> downloader = RTorrent.loadFromConfig(client, downloaderSection);
}
return downloader;
@@ -183,6 +195,7 @@ public Downloader createDownloader(String client, JsonObject downloaderSection)
downloader = Transmission.loadFromConfig(client, pbhServerAddress, downloaderSection);
case "biglybt" -> downloader = BiglyBT.loadFromConfig(client, downloaderSection);
case "deluge" -> downloader = Deluge.loadFromConfig(client, downloaderSection);
+ //case "rtorrent" -> downloader = RTorrent.loadFromConfig(client, downloaderSection);
}
return downloader;
@@ -217,12 +230,13 @@ private void setupIPDB() {
String databaseASN = mainConfig.getString("ip-database.database-asn", "");
boolean autoUpdate = mainConfig.getBoolean("ip-database.auto-update");
if (accountId.isEmpty() || licenseKey.isEmpty() || databaseCity.isEmpty() || databaseASN.isEmpty()) {
- log.warn(Lang.IPDB_NEED_CONFIG);
+ log.warn(tlUI(Lang.IPDB_NEED_CONFIG));
return;
}
- this.ipdb = new IPDB(new File(Main.getDataDirectory(), "ipdb"), accountId, licenseKey, databaseCity, databaseASN, autoUpdate);
+ this.ipdb = new IPDB(new File(Main.getDataDirectory(), "ipdb"), accountId, licenseKey,
+ databaseCity, databaseASN, autoUpdate, Main.getUserAgent());
} catch (Exception e) {
- log.info(Lang.IPDB_INVALID, e);
+ log.info(tlUI(Lang.IPDB_INVALID, e));
}
}
@@ -233,25 +247,10 @@ private void resetKnownDownloaders() {
downloader.setBanList(Collections.emptyList(), null, null);
}
} catch (Exception e) {
- log.warn(Lang.RESET_DOWNLOADER_FAILED, e);
+ log.error(tlUI(Lang.RESET_DOWNLOADER_FAILED), e);
}
}
- public void setupBtn() {
- if (this.btnNetwork != null) {
- this.btnNetwork.close();
- }
- BtnNetwork btnm;
- try {
- log.info(Lang.BTN_NETWORK_CONNECTING);
- btnm = new BtnNetwork(this, mainConfig.getConfigurationSection("btn"));
- log.info(Lang.BTN_NETWORK_ENABLED);
- } catch (IllegalStateException e) {
- btnm = null;
- log.info(Lang.BTN_NETWORK_NOT_ENABLED);
- }
- this.btnNetwork = btnm;
- }
private void registerBanListInvokers() {
banListInvoker.add(new IPFilterInvoker(this));
@@ -260,14 +259,14 @@ private void registerBanListInvokers() {
public void shutdown() {
// place some clean code here
- dumpBanListToFile();
- log.info(Lang.SHUTDOWN_CLOSE_METRICS);
+ saveBanList();
+ log.info(tlUI(Lang.SHUTDOWN_CLOSE_METRICS));
this.metrics.close();
- log.info(Lang.SHUTDOWN_UNREGISTER_MODULES);
+ log.info(tlUI(Lang.SHUTDOWN_UNREGISTER_MODULES));
this.moduleManager.unregisterAll();
- log.info(Lang.SHUTDOWN_CLOSE_DATABASE);
+ log.info(tlUI(Lang.SHUTDOWN_CLOSE_DATABASE));
this.databaseManager.close();
- log.info(Lang.SHUTDOWN_CLEANUP_RESOURCES);
+ log.info(tlUI(Lang.SHUTDOWN_CLEANUP_RESOURCES));
this.moduleMatchCache.close();
if (this.ipdb != null) {
this.ipdb.close();
@@ -279,7 +278,7 @@ public void shutdown() {
log.error("Failed to close download {}", d.getName(), e);
}
});
- log.info(Lang.SHUTDOWN_DONE);
+ log.info(tlUI(Lang.SHUTDOWN_DONE));
}
private void loadBanListToMemory() {
@@ -287,17 +286,9 @@ private void loadBanListToMemory() {
return;
}
try {
- if (!banListFile.exists()) {
- return;
- }
- String json = Files.readString(banListFile.toPath(), StandardCharsets.UTF_8);
- Map data = JsonUtil.getGson().fromJson(json, new TypeToken