Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<h1>SubBoost</h1>
<p>
<img src="https://img.shields.io/badge/platform-Linux%20%2B%20Docker-lightgrey.svg" alt="平台:Linux + Docker">
<img src="https://img.shields.io/badge/version-2.5.0-green.svg" alt="版本 2.5.0">
<img src="https://img.shields.io/badge/version-2.5.1-green.svg" alt="版本 2.5.1">
<a href="https://subboost.org"><img src="https://img.shields.io/badge/app-subboost.org-brightgreen.svg" alt="在线入口"></a>
<a href="https://docs.subboost.org"><img src="https://img.shields.io/badge/docs-subboost.org-blue.svg" alt="文档"></a>
<img src="https://img.shields.io/badge/image-GHCR-blue.svg" alt="GHCR 镜像">
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<h1>SubBoost</h1>
<p>
<img src="https://img.shields.io/badge/platform-Linux%20%2B%20Docker-lightgrey.svg" alt="Platform: Linux + Docker">
<img src="https://img.shields.io/badge/version-2.5.0-green.svg" alt="Version 2.5.0">
<img src="https://img.shields.io/badge/version-2.5.1-green.svg" alt="Version 2.5.1">
<a href="https://subboost.org"><img src="https://img.shields.io/badge/app-subboost.org-brightgreen.svg" alt="Online app"></a>
<a href="https://docs.subboost.org"><img src="https://img.shields.io/badge/docs-subboost.org-blue.svg" alt="Documentation"></a>
<img src="https://img.shields.io/badge/image-GHCR-blue.svg" alt="GHCR image">
Expand Down
40 changes: 17 additions & 23 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,43 @@
# SubBoost v2.5.0
# SubBoost v2.5.1

## 中文

### 更新重点

SubBoost v2.5.0 主要改善代理组编辑、自部署更新和订阅生成稳定性。建议 v2.4.0 用户升级。
SubBoost v2.5.1 是一个修复版本,主要修复自定义代理组的默认成员逻辑,并提升自部署安装、更新时的版本来源稳定性。建议 v2.5.0 用户升级。

### 主要变化

- **移除筛选代理组**,相关功能转移到分流代理组高级模式和自定义分流组。
- 分流代理组高级模式支持按来源、地区、关键词和排除条件整理节点。
- 订阅生成更稳,规则顺序、代理组输出和常见 Mihomo 字段处理减少了意外变化。
- 节点导入兼容性更好,覆盖更多常见节点链接和 Mihomo YAML 配置。
- 自部署安装和更新流程更可靠,`subboost update`、状态检查和失败提示都有改进。
- Dashboard 下载订阅 YAML 的行为更接近直接访问订阅链接,文件名和响应头更稳定。
- 首次安装后的管理员初始化、登录和数据库连接更稳,减少安装完成后进不去后台的情况。
- 安全和发布检查加强,降低公开包、安装资产和更新流程出错的风险。
- 修复新建自定义代理组时默认成员过宽的问题,避免默认生成出“节点选择”和自定义组互相引用的循环配置。
- `🚀 节点选择` 默认不再自动包含自定义代理组,减少代理组套娃导致配置异常或预览卡顿的风险。
- 新建普通自定义代理组默认只包含 `DIRECT`、`REJECT` 和真实节点,保留高级模式里的手动配置能力。
- 改进自部署安装和 `subboost update` 的版本来源处理,稳定版安装资产会默认固定到对应 release 版本。
- 改进自部署管理脚本的更新来源,减少从指定版本更新时意外跟随其它发布通道的风险。

### 升级说明

- 建议升级前备份 `/opt/subboost/.env` 和数据库,方便需要时回滚。
- 已安装 v2.4.0 的自部署实例可以继续使用 `subboost update` 更新。
- 已安装 v2.5.0 的自部署实例可以继续使用 `subboost update` 更新。
- 普通订阅转换、模板和规则功能不需要手动改环境变量。
- 如果你在 v2.4.0 使用过筛选代理组,请升级后到自定义代理组或分流代理组高级模式里重新配置
- 已经手动配置过的高级代理组不会被自动改写;本次主要改变新建和默认生成行为

## English

### Highlights

SubBoost v2.5.0 mainly improves proxy group editing, self-hosted updates, and subscription generation stability. v2.4.0 users are encouraged to upgrade.
SubBoost v2.5.1 is a patch release that fixes default custom proxy group membership and improves version-source stability for self-hosted install and update flows. v2.5.0 users are encouraged to upgrade.

### Main Changes

- Removed filtered proxy groups. Related features have moved to proxy group advanced mode and custom proxy groups.
- Proxy group advanced mode supports organizing nodes by source, region, keyword, and exclusion rules.
- Made subscription generation more stable, reducing unexpected changes in rule order, proxy group output, and common Mihomo fields.
- Improved node import compatibility for more common node links and Mihomo YAML configurations.
- Made self-hosted install and update flows more reliable, including `subboost update`, status checks, and failure messages.
- Dashboard YAML downloads now behave more like direct subscription links, with steadier filenames and response headers.
- Improved first-install admin setup, login, and database connection reliability to reduce post-install access issues.
- Strengthened safety and release checks to reduce the risk of problems in public packages, install assets, and updates.
- Fixed overly broad default members for newly created custom proxy groups, preventing default configurations where node selection and custom groups reference each other in a loop.
- `🚀 节点选择` no longer automatically includes custom proxy groups by default, reducing the risk of nested proxy group loops causing invalid output or preview slowdowns.
- Newly created normal custom proxy groups now default to `DIRECT`, `REJECT`, and real nodes only, while manual advanced configuration remains available.
- Improved self-hosted install and `subboost update` version-source handling so stable release assets stay pinned to the matching release version by default.
- Improved the self-hosted manager update source to reduce the risk of a pinned-version update unexpectedly following another release channel.

### Upgrade Notes

- Back up `/opt/subboost/.env` and the database before upgrading so rollback is easier if needed.
- Existing v2.4.0 self-hosted installations can continue to update with `subboost update`.
- Existing v2.5.0 self-hosted installations can continue to update with `subboost update`.
- Normal subscription conversion, templates, and rules do not require manual environment-variable changes.
- If you used filtered proxy groups in v2.4.0, reconfigure them after upgrading through custom proxy groups or proxy group advanced mode.
- Manually configured advanced proxy groups are not rewritten automatically; this release mainly changes new and default generated behavior.
2 changes: 1 addition & 1 deletion local/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@subboost/local",
"version": "2.5.0",
"version": "2.5.1",
"license": "AGPL-3.0-only",
"private": true,
"type": "module",
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "subboost",
"version": "2.5.0",
"version": "2.5.1",
"license": "AGPL-3.0-only",
"private": true,
"engines": {
Expand Down
71 changes: 60 additions & 11 deletions packages/core/src/generator/proxy-groups.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,55 @@ describe("proxy group generator", () => {
});
});

it("keeps filtered-node custom groups node-scoped while exposing them as policy members", () => {
it("defaults ordinary custom groups to node-only members without adding them to node select", () => {
const groups = generateProxyGroups({
nodes: [node("Node A"), node("Node B")],
enabledModules: ["select", "auto"],
ruleProviderBaseUrl: "https://rules.example.com",
testUrl: "https://probe.example.com/204",
testInterval: 120,
customProxyGroups: [
customGroup("one", "select"),
customGroup("two", "select"),
customGroup("direct", "direct-first"),
customGroup("reject", "reject-first"),
],
});

expect(groups.find((group) => group.name === "🚀 节点选择")?.proxies).toEqual([
"⚡ 自动选择",
"DIRECT",
"REJECT",
"Node A",
"Node B",
]);
expect(groups.find((group) => group.name === "Custom one")?.proxies).toEqual([
"DIRECT",
"REJECT",
"Node A",
"Node B",
]);
expect(groups.find((group) => group.name === "Custom two")?.proxies).toEqual([
"DIRECT",
"REJECT",
"Node A",
"Node B",
]);
expect(groups.find((group) => group.name === "Custom direct")?.proxies).toEqual([
"DIRECT",
"REJECT",
"Node A",
"Node B",
]);
expect(groups.find((group) => group.name === "Custom reject")?.proxies).toEqual([
"REJECT",
"DIRECT",
"Node A",
"Node B",
]);
});

it("keeps filtered-node custom groups node-scoped while exposing them as non-select policy members", () => {
const groups = generateProxyGroups({
nodes: [node("Node A"), node("Node B"), node("Filtered")],
proxyProviderNames: ["remote"],
Expand All @@ -152,7 +200,7 @@ describe("proxy group generator", () => {
customProxyGroups: [
{
id: "filtered",
name: "Filtered",
name: "Filtered Group",
emoji: "",
memberSource: "filtered-nodes",
includeInGroupMembers: true,
Expand All @@ -163,25 +211,26 @@ describe("proxy group generator", () => {
],
});

expect(groups[0]?.name).toBe("Filtered");
expect(groups.find((group) => group.name === "Filtered")?.proxies).toEqual([
expect(groups[0]?.name).toBe("Filtered Group");
expect(groups.find((group) => group.name === "Filtered Group")?.proxies).toEqual([
"DIRECT",
"REJECT",
"Node A",
"Filtered",
]);
expect(groups.find((group) => group.name === "Filtered")).not.toHaveProperty("use");
expect(groups.find((group) => group.name === "Filtered")?.proxies).not.toContain("Custom normal");
expect(groups.find((group) => group.name === "Filtered")?.proxies).not.toContain("🚀 节点选择");
expect(groups.find((group) => group.name === "Custom normal")?.proxies).toContain("Filtered");
expect(groups.find((group) => group.name === "Filtered Group")).not.toHaveProperty("use");
expect(groups.find((group) => group.name === "Filtered Group")?.proxies).not.toContain("Custom normal");
expect(groups.find((group) => group.name === "Filtered Group")?.proxies).not.toContain("🚀 节点选择");
expect(groups.find((group) => group.name === "Custom normal")?.proxies).not.toContain("Filtered Group");
expect(groups.find((group) => group.name === "Custom normal")?.proxies).not.toContain("🚀 节点选择");
expect(groups.find((group) => group.name === "Custom normal")?.proxies).not.toContain("Custom normal");
expect(groups.find((group) => group.name === "Custom normal")).toMatchObject({ use: ["remote"] });
expect(groups.find((group) => group.name === "🚀 节点选择")?.proxies).toContain("Filtered");
expect(groups.find((group) => group.name === "🚀 节点选择")?.proxies).toContain("Custom normal");
expect(groups.find((group) => group.name === "🚀 节点选择")?.proxies).not.toContain("Filtered Group");
expect(groups.find((group) => group.name === "🚀 节点选择")?.proxies).not.toContain("Custom normal");
expect(groups.find((group) => group.name === "🏠 私有网络")?.proxies?.slice(0, 5)).toEqual([
"DIRECT",
"REJECT",
"Filtered",
"Filtered Group",
"Custom normal",
"🚀 节点选择",
]);
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/generator/proxy-groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export function generateProxyGroups(options: GenerateOptions): ProxyGroup[] {
"DIRECT",
"REJECT"
);
const customBaseProxies = moduleBaseProxies;
const customBaseProxies = fallbackTargets("DIRECT", "REJECT", ...filteredNodeNames);
const availableMemberProxyNames = fallbackTargets(
"DIRECT",
"REJECT",
Expand Down Expand Up @@ -252,7 +252,6 @@ export function generateProxyGroups(options: GenerateOptions): ProxyGroup[] {
autoTarget,
"DIRECT",
"REJECT",
...customGroupNames,
...nodeNames
);
groups.push({
Expand Down Expand Up @@ -408,7 +407,7 @@ export function generateProxyGroups(options: GenerateOptions): ProxyGroup[] {
name: customGroup.name,
type: "select",
proxies: resolveCustom(
fallbackTargets("DIRECT", "REJECT", selectTarget, autoTarget, ...filteredNodeNames).filter(
fallbackTargets("DIRECT", "REJECT", ...filteredNodeNames).filter(
(target) => target !== customGroup.name
)
),
Expand All @@ -420,7 +419,7 @@ export function generateProxyGroups(options: GenerateOptions): ProxyGroup[] {
name: customGroup.name,
type: "select",
proxies: resolveCustom(
fallbackTargets("REJECT", "DIRECT", selectTarget).filter(
fallbackTargets("REJECT", "DIRECT", ...filteredNodeNames).filter(
(target) => target !== customGroup.name
)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe("custom config-store actions", () => {
const groupId = getState().customProxyGroups[0].id;
expect(groupId).toBe("custom-group-1767225600000");
expect(getState().customProxyGroups[0]).toEqual(
expect.objectContaining({ includeInGroupMembers: true })
expect.objectContaining({ includeInGroupMembers: false })
);

actions.updateCustomProxyGroup(groupId, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export function createCustomActions(
...(typeof group.description === "string" ? { description: group.description.trim() } : {}),
...(group.memberSource === "filtered-nodes" ? { memberSource: "filtered-nodes" as const } : {}),
includeInGroupMembers:
typeof group.includeInGroupMembers === "boolean" ? group.includeInGroupMembers : true,
typeof group.includeInGroupMembers === "boolean" ? group.includeInGroupMembers : false,
groupType: group.groupType,
...(group.groupType === "load-balance" && group.strategy ? { strategy: group.strategy } : {}),
...(group.advanced ? { advanced: normalizeProxyGroupAdvancedConfig(group.advanced) } : {}),
Expand Down
Loading