mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-09-19 00:13:03 +00:00
Fix: Shadowrocket link using base64 encoding (#3489)
This commit is contained in:
parent
db7e7dcd29
commit
0a58b5e745
2 changed files with 30 additions and 13 deletions
|
@ -50,7 +50,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawQR(value) {
|
function drawQR(value) {
|
||||||
try { new QRious({ element: document.getElementById('qrcode'), value, size: 220 }); } catch (e) { console.warn(e); }
|
try {
|
||||||
|
new QRious({ element: document.getElementById('qrcode'), value, size: 220 });
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to extract a human label (email/ps) from different link types
|
// Try to extract a human label (email/ps) from different link types
|
||||||
|
@ -61,22 +65,18 @@
|
||||||
if (json.ps) return json.ps;
|
if (json.ps) return json.ps;
|
||||||
if (json.add && json.id) return json.add; // fallback host
|
if (json.add && json.id) return json.add; // fallback host
|
||||||
} else if (link.startsWith('vless://') || link.startsWith('trojan://')) {
|
} else if (link.startsWith('vless://') || link.startsWith('trojan://')) {
|
||||||
// vless://<id>@host:port?...#name
|
|
||||||
const hashIdx = link.indexOf('#');
|
const hashIdx = link.indexOf('#');
|
||||||
if (hashIdx !== -1) return decodeURIComponent(link.substring(hashIdx + 1));
|
if (hashIdx !== -1) return decodeURIComponent(link.substring(hashIdx + 1));
|
||||||
// email sometimes in query params like sni or remark
|
|
||||||
const qIdx = link.indexOf('?');
|
const qIdx = link.indexOf('?');
|
||||||
if (qIdx !== -1) {
|
if (qIdx !== -1) {
|
||||||
const qs = new URL('http://x/?' + link.substring(qIdx + 1, hashIdx !== -1 ? hashIdx : undefined)).searchParams;
|
const qs = new URL('http://x/?' + link.substring(qIdx + 1, hashIdx !== -1 ? hashIdx : undefined)).searchParams;
|
||||||
if (qs.get('remark')) return qs.get('remark');
|
if (qs.get('remark')) return qs.get('remark');
|
||||||
if (qs.get('email')) return qs.get('email');
|
if (qs.get('email')) return qs.get('email');
|
||||||
}
|
}
|
||||||
// else take user@host
|
|
||||||
const at = link.indexOf('@');
|
const at = link.indexOf('@');
|
||||||
const protSep = link.indexOf('://');
|
const protSep = link.indexOf('://');
|
||||||
if (at !== -1 && protSep !== -1) return link.substring(protSep + 3, at);
|
if (at !== -1 && protSep !== -1) return link.substring(protSep + 3, at);
|
||||||
} else if (link.startsWith('ss://')) {
|
} else if (link.startsWith('ss://')) {
|
||||||
// shadowsocks: label often after #
|
|
||||||
const hashIdx = link.indexOf('#');
|
const hashIdx = link.indexOf('#');
|
||||||
if (hashIdx !== -1) return decodeURIComponent(link.substring(hashIdx + 1));
|
if (hashIdx !== -1) return decodeURIComponent(link.substring(hashIdx + 1));
|
||||||
}
|
}
|
||||||
|
@ -96,14 +96,13 @@
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.lang = LanguageManager.getLanguage();
|
this.lang = LanguageManager.getLanguage();
|
||||||
// Discover subJsonUrl if provided via template bootstrap
|
|
||||||
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;
|
||||||
drawQR(this.app.subUrl);
|
drawQR(this.app.subUrl);
|
||||||
// Draw second QR if available
|
try {
|
||||||
try { new QRious({ element: document.getElementById('qrcode-subjson'), value: this.app.subJsonUrl || '', size: 220 }); } catch (e) { /* ignore */ }
|
new QRious({ element: document.getElementById('qrcode-subjson'), value: this.app.subJsonUrl || '', size: 220 });
|
||||||
// Track viewport width for responsive behavior
|
} 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);
|
||||||
},
|
},
|
||||||
|
@ -111,15 +110,33 @@
|
||||||
if (this._onResize) window.removeEventListener('resize', this._onResize);
|
if (this._onResize) window.removeEventListener('resize', this._onResize);
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isMobile() { return this.viewportWidth < 576; },
|
isMobile() {
|
||||||
isUnlimited() { return !this.app.totalByte; },
|
return this.viewportWidth < 576;
|
||||||
|
},
|
||||||
|
isUnlimited() {
|
||||||
|
return !this.app.totalByte;
|
||||||
|
},
|
||||||
isActive() {
|
isActive() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const expiryOk = !this.app.expireMs || this.app.expireMs >= now;
|
const expiryOk = !this.app.expireMs || this.app.expireMs >= now;
|
||||||
const trafficOk = !this.app.totalByte || (this.app.uploadByte + this.app.downloadByte) <= this.app.totalByte;
|
const trafficOk = !this.app.totalByte || (this.app.uploadByte + this.app.downloadByte) <= this.app.totalByte;
|
||||||
return expiryOk && trafficOk;
|
return expiryOk && trafficOk;
|
||||||
},
|
},
|
||||||
|
shadowrocketUrl() {
|
||||||
|
const rawUrl = this.app.subUrl + '?flag=shadowrocket';
|
||||||
|
const base64Url = btoa(rawUrl);
|
||||||
|
const remark = encodeURIComponent(this.app.sId || 'Subscription');
|
||||||
|
return `shadowrocket://add/sub/${base64Url}?remark=${remark}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderLink,
|
||||||
|
copy,
|
||||||
|
open,
|
||||||
|
linkName,
|
||||||
|
i18nLabel(key) {
|
||||||
|
return '{{ i18n "' + key + '" }}';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: { renderLink, copy, open, linkName, i18nLabel(key) { return '{{ i18n "' + key + '" }}'; } },
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -233,7 +233,7 @@
|
||||||
<a-menu slot="overlay"
|
<a-menu slot="overlay"
|
||||||
:class="themeSwitcher.currentTheme">
|
:class="themeSwitcher.currentTheme">
|
||||||
<a-menu-item key="ios-shadowrocket"
|
<a-menu-item key="ios-shadowrocket"
|
||||||
@click="open('shadowrocket://add/subscription?url=' + encodeURIComponent(app.subUrl) + '&remark=' + encodeURIComponent(app.sId))">Shadowrocket</a-menu-item>
|
@click="open(shadowrocketUrl)">Shadowrocket</a-menu-item>
|
||||||
<a-menu-item key="ios-v2box"
|
<a-menu-item key="ios-v2box"
|
||||||
@click="open('v2box://install-sub?url=' + encodeURIComponent(app.subUrl) + '&name=' + encodeURIComponent(app.sId))">V2Box</a-menu-item>
|
@click="open('v2box://install-sub?url=' + encodeURIComponent(app.subUrl) + '&name=' + encodeURIComponent(app.sId))">V2Box</a-menu-item>
|
||||||
<a-menu-item key="ios-streisand"
|
<a-menu-item key="ios-streisand"
|
||||||
|
|
Loading…
Reference in a new issue