mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-07 13:44:24 +00:00
fix: repair clash subscription toggle and separate clash path settings
This commit is contained in:
parent
67bf6c1a9a
commit
d6de00cd00
6 changed files with 107 additions and 17 deletions
|
|
@ -0,0 +1,65 @@
|
||||||
|
Task Record
|
||||||
|
|
||||||
|
Date: 2026-04-26
|
||||||
|
Related Module: web settings / subscription assets / setting service
|
||||||
|
Change Type: Fix
|
||||||
|
|
||||||
|
Background
|
||||||
|
|
||||||
|
The Clash subscription toggle in `/panel/settings` did not behave correctly in practice.
|
||||||
|
The page was loading an outdated fingerprinted frontend model from `web/public` that did not include Clash subscription fields, which caused inconsistent binding and update behavior for `subClashEnable` and related properties.
|
||||||
|
In addition, Clash subscription keys were not mapped in the settings grouping metadata, making their nested config representation inconsistent.
|
||||||
|
|
||||||
|
Changes
|
||||||
|
|
||||||
|
Regenerated fingerprinted frontend assets via `go run ./cmd/genassets` so the settings page now loads an updated `AllSetting` model containing:
|
||||||
|
- `subClashEnable`
|
||||||
|
- `subClashPath`
|
||||||
|
- `subClashURI`
|
||||||
|
|
||||||
|
Updated settings group mappings in `web/service/setting.go`:
|
||||||
|
- Added Clash keys to `subscriptionNetwork`:
|
||||||
|
- `clashEnable -> subClashEnable`
|
||||||
|
- `clashPath -> subClashPath`
|
||||||
|
- `clashURI -> subClashURI`
|
||||||
|
- Added corresponding Clash keys to legacy `sub` mapping for compatibility.
|
||||||
|
|
||||||
|
Impact
|
||||||
|
|
||||||
|
Affected modules or files.
|
||||||
|
- `web/service/setting.go`
|
||||||
|
- `web/public/assets-manifest.json`
|
||||||
|
- `web/public/assets/js/model/setting.*.js` (fingerprinted replacement)
|
||||||
|
- `web/public/assets/js/subscription.*.js` (fingerprinted replacement)
|
||||||
|
- `web/public/assets/codemirror/yaml.*.js` (fingerprinted generated asset)
|
||||||
|
|
||||||
|
Whether APIs, database, config, build, or compatibility are affected.
|
||||||
|
- API schema unchanged.
|
||||||
|
- Database unchanged.
|
||||||
|
- Settings file structure compatibility improved for Clash keys in nested/legacy mappings.
|
||||||
|
- Frontend static asset fingerprints updated.
|
||||||
|
|
||||||
|
Whether upstream or downstream callers are affected.
|
||||||
|
- Panel settings frontend now correctly tracks and submits Clash subscription toggle/path fields.
|
||||||
|
- Subscription page uses refreshed asset bundle.
|
||||||
|
|
||||||
|
Verification
|
||||||
|
|
||||||
|
List validation commands or checks performed.
|
||||||
|
- `go run ./cmd/genassets`
|
||||||
|
- `go test -race ./web/service/...`
|
||||||
|
|
||||||
|
State the result.
|
||||||
|
- Asset generation succeeded.
|
||||||
|
- Related service tests passed.
|
||||||
|
|
||||||
|
If not verified, explain why.
|
||||||
|
- No live runtime verification against a deployed panel/subscription server was performed in this local environment.
|
||||||
|
|
||||||
|
Risks And Follow-Up
|
||||||
|
|
||||||
|
Remaining risks.
|
||||||
|
- Existing browser caches may keep old fingerprint mappings until refresh; after deploy/restart, hard refresh may still be needed in some clients.
|
||||||
|
|
||||||
|
Recommended follow-up work.
|
||||||
|
- Verify in a deployed environment that toggling `Clash Subscription` and editing `subClashPath` immediately reflects expected behavior after restart/reload cycle.
|
||||||
|
|
@ -19,14 +19,15 @@
|
||||||
"codemirror/lint/lint.css": "codemirror/lint/lint.5902b061.css",
|
"codemirror/lint/lint.css": "codemirror/lint/lint.5902b061.css",
|
||||||
"codemirror/lint/lint.js": "codemirror/lint/lint.8d16a6b4.js",
|
"codemirror/lint/lint.js": "codemirror/lint/lint.8d16a6b4.js",
|
||||||
"codemirror/xq.min.css": "codemirror/xq.min.18b37ea8.css",
|
"codemirror/xq.min.css": "codemirror/xq.min.18b37ea8.css",
|
||||||
|
"codemirror/yaml.js": "codemirror/yaml.7f87153b.js",
|
||||||
"css/custom.min.css": "css/custom.min.7a7540cc.css",
|
"css/custom.min.css": "css/custom.min.7a7540cc.css",
|
||||||
"js/axios-init.js": "js/axios-init.5d82a152.js",
|
"js/axios-init.js": "js/axios-init.5d82a152.js",
|
||||||
"js/model/dbinbound.js": "js/model/dbinbound.2232a7d6.js",
|
"js/model/dbinbound.js": "js/model/dbinbound.2232a7d6.js",
|
||||||
"js/model/inbound.js": "js/model/inbound.afa36664.js",
|
"js/model/inbound.js": "js/model/inbound.afa36664.js",
|
||||||
"js/model/outbound.js": "js/model/outbound.5b13ad17.js",
|
"js/model/outbound.js": "js/model/outbound.5b13ad17.js",
|
||||||
"js/model/reality_targets.js": "js/model/reality_targets.6ca38c21.js",
|
"js/model/reality_targets.js": "js/model/reality_targets.6ca38c21.js",
|
||||||
"js/model/setting.js": "js/model/setting.3c69ea22.js",
|
"js/model/setting.js": "js/model/setting.a37c1aec.js",
|
||||||
"js/subscription.js": "js/subscription.d3d8665a.js",
|
"js/subscription.js": "js/subscription.84ade419.js",
|
||||||
"js/util/index.js": "js/util/index.7ced4edf.js",
|
"js/util/index.js": "js/util/index.7ced4edf.js",
|
||||||
"js/websocket.js": "js/websocket.938f634e.js",
|
"js/websocket.js": "js/websocket.938f634e.js",
|
||||||
"moment/moment-jalali.min.js": "moment/moment-jalali.min.9d3db244.js",
|
"moment/moment-jalali.min.js": "moment/moment-jalali.min.9d3db244.js",
|
||||||
|
|
|
||||||
1
web/public/assets/codemirror/yaml.7f87153b.js
Normal file
1
web/public/assets/codemirror/yaml.7f87153b.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(e){"use strict";e.defineMode("yaml",function(){var n=new RegExp("\\b(("+["true","false","on","off","yes","no"].join(")|(")+"))$","i");return{token:function(e,i){var t=e.peek(),r=i.escaped;if(i.escaped=!1,"#"==t&&(0==e.pos||/\s/.test(e.string.charAt(e.pos-1))))return e.skipToEnd(),"comment";if(e.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/))return"string";if(i.literal&&e.indentation()>i.keyCol)return e.skipToEnd(),"string";if(i.literal&&(i.literal=!1),e.sol()){if(i.keyCol=0,i.pair=!1,i.pairStart=!1,e.match("---"))return"def";if(e.match("..."))return"def";if(e.match(/\s*-\s+/))return"meta"}if(e.match(/^(\{|\}|\[|\])/))return"{"==t?i.inlinePairs++:"}"==t?i.inlinePairs--:"["==t?i.inlineList++:i.inlineList--,"meta";if(0<i.inlineList&&!r&&","==t)return e.next(),"meta";if(0<i.inlinePairs&&!r&&","==t)return i.keyCol=0,i.pair=!1,i.pairStart=!1,e.next(),"meta";if(i.pairStart){if(e.match(/^\s*(\||\>)\s*/))return i.literal=!0,"meta";if(e.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i))return"variable-2";if(0==i.inlinePairs&&e.match(/^\s*-?[0-9\.\,]+\s?$/))return"number";if(0<i.inlinePairs&&e.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/))return"number";if(e.match(n))return"keyword"}return!i.pair&&e.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)?(i.pair=!0,i.keyCol=e.indentation(),"atom"):i.pair&&e.match(/^:\s*/)?(i.pairStart=!0,"meta"):(i.pairStart=!1,i.escaped="\\"==t,e.next(),null)},startState:function(){return{pair:!1,pairStart:!1,keyCol:0,inlinePairs:0,inlineList:0,literal:!1,escaped:!1}},lineComment:"#",fold:"indent"}}),e.defineMIME("text/x-yaml","yaml"),e.defineMIME("text/yaml","yaml")});
|
||||||
|
|
@ -52,6 +52,10 @@ class AllSetting {
|
||||||
this.subJsonNoises = "";
|
this.subJsonNoises = "";
|
||||||
this.subJsonMux = "";
|
this.subJsonMux = "";
|
||||||
this.subJsonRules = "";
|
this.subJsonRules = "";
|
||||||
|
this.subClashEnable = false;
|
||||||
|
this.subClashPath = "/clash/";
|
||||||
|
this.subClashURI = "";
|
||||||
|
this.subClashTemplate = "";
|
||||||
|
|
||||||
this.timeLocation = "Local";
|
this.timeLocation = "Local";
|
||||||
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
sId: el.getAttribute('data-sid') || '',
|
sId: el.getAttribute('data-sid') || '',
|
||||||
subUrl: el.getAttribute('data-sub-url') || '',
|
subUrl: el.getAttribute('data-sub-url') || '',
|
||||||
subJsonUrl: el.getAttribute('data-subjson-url') || '',
|
subJsonUrl: el.getAttribute('data-subjson-url') || '',
|
||||||
|
subClashUrl: el.getAttribute('data-subclash-url') || '',
|
||||||
download: el.getAttribute('data-download') || '',
|
download: el.getAttribute('data-download') || '',
|
||||||
upload: el.getAttribute('data-upload') || '',
|
upload: el.getAttribute('data-upload') || '',
|
||||||
used: el.getAttribute('data-used') || '',
|
used: el.getAttribute('data-used') || '',
|
||||||
|
|
@ -99,6 +100,8 @@
|
||||||
const tpl = document.getElementById('subscription-data');
|
const tpl = document.getElementById('subscription-data');
|
||||||
const sj = tpl ? tpl.getAttribute('data-subjson-url') : '';
|
const sj = tpl ? tpl.getAttribute('data-subjson-url') : '';
|
||||||
if (sj) this.app.subJsonUrl = sj;
|
if (sj) this.app.subJsonUrl = sj;
|
||||||
|
const sc = tpl ? tpl.getAttribute('data-subclash-url') : '';
|
||||||
|
if (sc) this.app.subClashUrl = sc;
|
||||||
drawQR(this.app.subUrl);
|
drawQR(this.app.subUrl);
|
||||||
try {
|
try {
|
||||||
const elJson = document.getElementById('qrcode-subjson');
|
const elJson = document.getElementById('qrcode-subjson');
|
||||||
|
|
@ -106,6 +109,12 @@
|
||||||
new QRious({ element: elJson, value: this.app.subJsonUrl, size: 220 });
|
new QRious({ element: elJson, value: this.app.subJsonUrl, size: 220 });
|
||||||
}
|
}
|
||||||
} catch (e) { /* ignore */ }
|
} catch (e) { /* ignore */ }
|
||||||
|
try {
|
||||||
|
const elClash = document.getElementById('qrcode-subclash');
|
||||||
|
if (elClash && this.app.subClashUrl) {
|
||||||
|
new QRious({ element: elClash, value: this.app.subClashUrl, size: 220 });
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
this._onResize = () => { this.viewportWidth = window.innerWidth; };
|
this._onResize = () => { this.viewportWidth = window.innerWidth; };
|
||||||
window.addEventListener('resize', this._onResize);
|
window.addEventListener('resize', this._onResize);
|
||||||
},
|
},
|
||||||
|
|
@ -145,6 +154,10 @@
|
||||||
},
|
},
|
||||||
happUrl() {
|
happUrl() {
|
||||||
return `happ://add/${this.app.subUrl}`;
|
return `happ://add/${this.app.subUrl}`;
|
||||||
|
},
|
||||||
|
clashvergeUrl() {
|
||||||
|
const url = this.app.subClashUrl || this.app.subUrl;
|
||||||
|
return `clash-verge://install-config?url=${encodeURIComponent(url)}&name=${encodeURIComponent(this.app.sId)}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -171,20 +171,23 @@ var settingGroups = map[string]map[string]string{
|
||||||
"lang": "tgLang",
|
"lang": "tgLang",
|
||||||
},
|
},
|
||||||
"subscriptionNetwork": {
|
"subscriptionNetwork": {
|
||||||
"enable": "subEnable",
|
"enable": "subEnable",
|
||||||
"jsonEnable": "subJsonEnable",
|
"jsonEnable": "subJsonEnable",
|
||||||
"listen": "subListen",
|
"clashEnable": "subClashEnable",
|
||||||
"port": "subPort",
|
"listen": "subListen",
|
||||||
"path": "subPath",
|
"port": "subPort",
|
||||||
"domain": "subDomain",
|
"path": "subPath",
|
||||||
"certFile": "subCertFile",
|
"jsonPath": "subJsonPath",
|
||||||
"keyFile": "subKeyFile",
|
"clashPath": "subClashPath",
|
||||||
"updates": "subUpdates",
|
"domain": "subDomain",
|
||||||
"encrypt": "subEncrypt",
|
"certFile": "subCertFile",
|
||||||
"showInfo": "subShowInfo",
|
"keyFile": "subKeyFile",
|
||||||
"uri": "subURI",
|
"updates": "subUpdates",
|
||||||
"jsonPath": "subJsonPath",
|
"encrypt": "subEncrypt",
|
||||||
"jsonURI": "subJsonURI",
|
"showInfo": "subShowInfo",
|
||||||
|
"uri": "subURI",
|
||||||
|
"jsonURI": "subJsonURI",
|
||||||
|
"clashURI": "subClashURI",
|
||||||
},
|
},
|
||||||
"subscriptionBranding": {
|
"subscriptionBranding": {
|
||||||
"title": "subTitle",
|
"title": "subTitle",
|
||||||
|
|
@ -271,6 +274,7 @@ var legacySettingGroups = map[string]map[string]string{
|
||||||
"sub": {
|
"sub": {
|
||||||
"enable": "subEnable",
|
"enable": "subEnable",
|
||||||
"jsonEnable": "subJsonEnable",
|
"jsonEnable": "subJsonEnable",
|
||||||
|
"clashEnable": "subClashEnable",
|
||||||
"title": "subTitle",
|
"title": "subTitle",
|
||||||
"supportUrl": "subSupportUrl",
|
"supportUrl": "subSupportUrl",
|
||||||
"profileUrl": "subProfileUrl",
|
"profileUrl": "subProfileUrl",
|
||||||
|
|
@ -280,6 +284,8 @@ var legacySettingGroups = map[string]map[string]string{
|
||||||
"listen": "subListen",
|
"listen": "subListen",
|
||||||
"port": "subPort",
|
"port": "subPort",
|
||||||
"path": "subPath",
|
"path": "subPath",
|
||||||
|
"jsonPath": "subJsonPath",
|
||||||
|
"clashPath": "subClashPath",
|
||||||
"domain": "subDomain",
|
"domain": "subDomain",
|
||||||
"certFile": "subCertFile",
|
"certFile": "subCertFile",
|
||||||
"keyFile": "subKeyFile",
|
"keyFile": "subKeyFile",
|
||||||
|
|
@ -287,8 +293,8 @@ var legacySettingGroups = map[string]map[string]string{
|
||||||
"encrypt": "subEncrypt",
|
"encrypt": "subEncrypt",
|
||||||
"showInfo": "subShowInfo",
|
"showInfo": "subShowInfo",
|
||||||
"uri": "subURI",
|
"uri": "subURI",
|
||||||
"jsonPath": "subJsonPath",
|
|
||||||
"jsonURI": "subJsonURI",
|
"jsonURI": "subJsonURI",
|
||||||
|
"clashURI": "subClashURI",
|
||||||
"jsonFragment": "subJsonFragment",
|
"jsonFragment": "subJsonFragment",
|
||||||
"jsonNoises": "subJsonNoises",
|
"jsonNoises": "subJsonNoises",
|
||||||
"jsonMux": "subJsonMux",
|
"jsonMux": "subJsonMux",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue