feat(sub): add dummy info node and status configs for subscriptions (#6412)

* feat(settings): add subInfoNodeEnable and status template settings

* feat(sub): add dummy info node and status configs for raw links

* feat(sub): support dummy info node in clash and json subscriptions

* feat(ui): add subscription info node switch and status templates to settings

* style: apply gofumpt formatting

* fix(sub): address review feedback on subscription info node

- Restore GetSubs contract to avoid unintended remark expansions on non-subscription-body calls.
- Exclude dummy info node from Clash PROXY select group when active server nodes exist.
- Track hasEnabledClient and set traffic.Enable in JSON and Clash paths so status tokens evaluate correctly.
- Deterministically sort client emails across subscriptions before selecting primaryEmail.
- Consolidate duplicated info-node evaluation logic into resolveInfoNodeRemark helper.
- Remove redundant pure-getter test from setting_sub_info_node_test.go.

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
Rouzbeh†
2026-09-08 18:44:01 +03:30
committed by GitHub
parent b8597314f8
commit 9f76a66dcf
33 changed files with 1374 additions and 35 deletions
+6
View File
@@ -58,9 +58,11 @@ export const EXAMPLES: Record<string, unknown> = {
"subEnable": false,
"subEnableRouting": false,
"subEncrypt": false,
"subExpiredTemplate": "",
"subHideSettings": false,
"subIncyEnableRouting": false,
"subIncyRoutingRules": "",
"subInfoNodeEnable": false,
"subJsonAlwaysArray": false,
"subJsonAutoDetect": false,
"subJsonEnable": false,
@@ -81,6 +83,7 @@ export const EXAMPLES: Record<string, unknown> = {
"subSupportUrl": "",
"subThemeDir": "",
"subTitle": "",
"subTrafficDepletedTemplate": "",
"subURI": "",
"subUpdates": 0,
"tgBotAPIServer": "",
@@ -172,9 +175,11 @@ export const EXAMPLES: Record<string, unknown> = {
"subEnable": false,
"subEnableRouting": false,
"subEncrypt": false,
"subExpiredTemplate": "",
"subHideSettings": false,
"subIncyEnableRouting": false,
"subIncyRoutingRules": "",
"subInfoNodeEnable": false,
"subJsonAlwaysArray": false,
"subJsonAutoDetect": false,
"subJsonEnable": false,
@@ -195,6 +200,7 @@ export const EXAMPLES: Record<string, unknown> = {
"subSupportUrl": "",
"subThemeDir": "",
"subTitle": "",
"subTrafficDepletedTemplate": "",
"subURI": "",
"subUpdates": 0,
"tgBotAPIServer": "",
+24
View File
@@ -191,6 +191,9 @@ export const SCHEMAS: Record<string, unknown> = {
"subEncrypt": {
"type": "boolean"
},
"subExpiredTemplate": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -200,6 +203,9 @@ export const SCHEMAS: Record<string, unknown> = {
"subIncyRoutingRules": {
"type": "string"
},
"subInfoNodeEnable": {
"type": "boolean"
},
"subJsonAlwaysArray": {
"type": "boolean"
},
@@ -262,6 +268,9 @@ export const SCHEMAS: Record<string, unknown> = {
"subTitle": {
"type": "string"
},
"subTrafficDepletedTemplate": {
"type": "string"
},
"subURI": {
"type": "string"
},
@@ -407,9 +416,11 @@ export const SCHEMAS: Record<string, unknown> = {
"subEnable",
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
"subInfoNodeEnable",
"subJsonAlwaysArray",
"subJsonAutoDetect",
"subJsonEnable",
@@ -430,6 +441,7 @@ export const SCHEMAS: Record<string, unknown> = {
"subSupportUrl",
"subThemeDir",
"subTitle",
"subTrafficDepletedTemplate",
"subURI",
"subUpdates",
"tgBotAPIServer",
@@ -670,6 +682,9 @@ export const SCHEMAS: Record<string, unknown> = {
"subEncrypt": {
"type": "boolean"
},
"subExpiredTemplate": {
"type": "string"
},
"subHideSettings": {
"type": "boolean"
},
@@ -679,6 +694,9 @@ export const SCHEMAS: Record<string, unknown> = {
"subIncyRoutingRules": {
"type": "string"
},
"subInfoNodeEnable": {
"type": "boolean"
},
"subJsonAlwaysArray": {
"type": "boolean"
},
@@ -741,6 +759,9 @@ export const SCHEMAS: Record<string, unknown> = {
"subTitle": {
"type": "string"
},
"subTrafficDepletedTemplate": {
"type": "string"
},
"subURI": {
"type": "string"
},
@@ -893,9 +914,11 @@ export const SCHEMAS: Record<string, unknown> = {
"subEnable",
"subEnableRouting",
"subEncrypt",
"subExpiredTemplate",
"subHideSettings",
"subIncyEnableRouting",
"subIncyRoutingRules",
"subInfoNodeEnable",
"subJsonAlwaysArray",
"subJsonAutoDetect",
"subJsonEnable",
@@ -916,6 +939,7 @@ export const SCHEMAS: Record<string, unknown> = {
"subSupportUrl",
"subThemeDir",
"subTitle",
"subTrafficDepletedTemplate",
"subURI",
"subUpdates",
"tgBotAPIServer",
+6
View File
@@ -66,9 +66,11 @@ export interface AllSetting {
subEnable: boolean;
subEnableRouting: boolean;
subEncrypt: boolean;
subExpiredTemplate: string;
subHideSettings: boolean;
subIncyEnableRouting: boolean;
subIncyRoutingRules: string;
subInfoNodeEnable: boolean;
subJsonAlwaysArray: boolean;
subJsonAutoDetect: boolean;
subJsonEnable: boolean;
@@ -89,6 +91,7 @@ export interface AllSetting {
subSupportUrl: string;
subThemeDir: string;
subTitle: string;
subTrafficDepletedTemplate: string;
subURI: string;
subUpdates: number;
tgBotAPIServer: string;
@@ -181,9 +184,11 @@ export interface AllSettingView {
subEnable: boolean;
subEnableRouting: boolean;
subEncrypt: boolean;
subExpiredTemplate: string;
subHideSettings: boolean;
subIncyEnableRouting: boolean;
subIncyRoutingRules: string;
subInfoNodeEnable: boolean;
subJsonAlwaysArray: boolean;
subJsonAutoDetect: boolean;
subJsonEnable: boolean;
@@ -204,6 +209,7 @@ export interface AllSettingView {
subSupportUrl: string;
subThemeDir: string;
subTitle: string;
subTrafficDepletedTemplate: string;
subURI: string;
subUpdates: number;
tgBotAPIServer: string;
+6
View File
@@ -82,9 +82,11 @@ export const AllSettingSchema = z.object({
subEnable: z.boolean(),
subEnableRouting: z.boolean(),
subEncrypt: z.boolean(),
subExpiredTemplate: z.string(),
subHideSettings: z.boolean(),
subIncyEnableRouting: z.boolean(),
subIncyRoutingRules: z.string(),
subInfoNodeEnable: z.boolean(),
subJsonAlwaysArray: z.boolean(),
subJsonAutoDetect: z.boolean(),
subJsonEnable: z.boolean(),
@@ -105,6 +107,7 @@ export const AllSettingSchema = z.object({
subSupportUrl: z.string(),
subThemeDir: z.string(),
subTitle: z.string(),
subTrafficDepletedTemplate: z.string(),
subURI: z.string(),
subUpdates: z.number().int().min(0).max(525600),
tgBotAPIServer: z.string(),
@@ -198,9 +201,11 @@ export const AllSettingViewSchema = z.object({
subEnable: z.boolean(),
subEnableRouting: z.boolean(),
subEncrypt: z.boolean(),
subExpiredTemplate: z.string(),
subHideSettings: z.boolean(),
subIncyEnableRouting: z.boolean(),
subIncyRoutingRules: z.string(),
subInfoNodeEnable: z.boolean(),
subJsonAlwaysArray: z.boolean(),
subJsonAutoDetect: z.boolean(),
subJsonEnable: z.boolean(),
@@ -221,6 +226,7 @@ export const AllSettingViewSchema = z.object({
subSupportUrl: z.string(),
subThemeDir: z.string(),
subTitle: z.string(),
subTrafficDepletedTemplate: z.string(),
subURI: z.string(),
subUpdates: z.number().int().min(0).max(525600),
tgBotAPIServer: z.string(),