Template
v1.7.0: pick server first, then protocol in create flows.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+113
-29
@@ -127,17 +127,22 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ _('server_label') }}</label>
|
||||
<select class="form-select" id="inviteServer" onchange="onInviteServerChange()">
|
||||
{% if xui_configured %}
|
||||
<option value="xui">3x-ui</option>
|
||||
{% endif %}
|
||||
{% for s in servers %}
|
||||
<option value="{{ loop.index0 }}">{{ s.name or s.host }} ({{ s.host }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="form-hint">{{ _('server_then_protocol_hint') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="inviteProtoGroup">
|
||||
<label class="form-label">{{ _('protocol_label') }}</label>
|
||||
<select class="form-select" id="inviteProtocol" onchange="updateInviteProtoFields()">
|
||||
<option value="xui">3x-ui VLESS</option>
|
||||
{% for s in servers %}
|
||||
{% set server_idx = loop.index0 %}
|
||||
{% for key, info in (s.protocols or {}).items() %}
|
||||
{% if info.installed and key.split('__')[0] in ['awg','awg2','awg_legacy','xray','wireguard','telemt'] %}
|
||||
<option value="{{ key }}|{{ server_idx }}">{{ s.name or s.host }} — {{ key }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<!-- filled by JS from selected server -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -187,9 +192,28 @@
|
||||
|
||||
<script>
|
||||
const invitesData = {{ invites | tojson }};
|
||||
const serversData = {{ servers | tojson }};
|
||||
const xuiConfigured = {{ 'true' if xui_configured else 'false' }};
|
||||
const xuiDefaultInbound = {{ xui_default_inbound | int }};
|
||||
const xuiDefaultPanelId = {{ (xui_default_panel_id or '') | tojson }};
|
||||
const VPN_PROTO_ORDER = ['awg2', 'awg', 'awg_legacy', 'wireguard', 'xray', 'telemt'];
|
||||
const PROTO_TITLES = {
|
||||
awg2: 'AmneziaWG 2.0',
|
||||
awg: 'AmneziaWG',
|
||||
awg_legacy: 'AWG Legacy',
|
||||
wireguard: 'WireGuard',
|
||||
xray: 'Xray (VLESS-Reality)',
|
||||
telemt: 'Telemt',
|
||||
xui: '3x-ui VLESS',
|
||||
};
|
||||
|
||||
function protoTitle(key) {
|
||||
const raw = String(key || '');
|
||||
const base = raw.split('__')[0];
|
||||
const title = PROTO_TITLES[base] || base.toUpperCase();
|
||||
const m = raw.match(/__(\d+)$/);
|
||||
return m ? `${title} #${m[1]}` : title;
|
||||
}
|
||||
|
||||
function inviteUrl(token) {
|
||||
return `${window.location.origin}/invite/${token}`;
|
||||
@@ -200,6 +224,57 @@
|
||||
showToast(_('copied'), 'success');
|
||||
}
|
||||
|
||||
function fillInviteProtocols(preferProtocol) {
|
||||
const serverVal = document.getElementById('inviteServer').value;
|
||||
const sel = document.getElementById('inviteProtocol');
|
||||
sel.innerHTML = '';
|
||||
|
||||
if (serverVal === 'xui') {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = 'xui';
|
||||
opt.textContent = PROTO_TITLES.xui;
|
||||
sel.appendChild(opt);
|
||||
updateInviteProtoFields();
|
||||
return;
|
||||
}
|
||||
|
||||
const server = serversData[parseInt(serverVal, 10)];
|
||||
const protocols = (server && server.protocols) || {};
|
||||
const installed = Object.keys(protocols).filter(k => {
|
||||
const info = protocols[k] || {};
|
||||
if (!info.installed) return false;
|
||||
return VPN_PROTO_ORDER.includes(k.split('__')[0]);
|
||||
});
|
||||
installed.sort((a, b) => {
|
||||
const ia = VPN_PROTO_ORDER.indexOf(a.split('__')[0]);
|
||||
const ib = VPN_PROTO_ORDER.indexOf(b.split('__')[0]);
|
||||
return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib) || a.localeCompare(b);
|
||||
});
|
||||
|
||||
if (!installed.length) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = '';
|
||||
opt.textContent = _('no_protocols');
|
||||
opt.disabled = true;
|
||||
sel.appendChild(opt);
|
||||
updateInviteProtoFields();
|
||||
return;
|
||||
}
|
||||
|
||||
installed.forEach(key => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = key;
|
||||
opt.textContent = protoTitle(key);
|
||||
if (preferProtocol && preferProtocol === key) opt.selected = true;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
updateInviteProtoFields();
|
||||
}
|
||||
|
||||
function onInviteServerChange() {
|
||||
fillInviteProtocols();
|
||||
}
|
||||
|
||||
function updateInviteProtoFields() {
|
||||
const v = document.getElementById('inviteProtocol').value;
|
||||
document.getElementById('inviteInboundGroup').style.display = (v === 'xui') ? '' : 'none';
|
||||
@@ -237,25 +312,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
function parseProtocolValue() {
|
||||
const raw = document.getElementById('inviteProtocol').value;
|
||||
if (raw === 'xui') return { protocol: 'xui', server_id: 0 };
|
||||
if (raw.includes('|')) {
|
||||
const [protocol, sid] = raw.split('|');
|
||||
return { protocol, server_id: parseInt(sid || '0') };
|
||||
function parseInviteSelection() {
|
||||
const serverVal = document.getElementById('inviteServer').value;
|
||||
const protocol = document.getElementById('inviteProtocol').value;
|
||||
if (serverVal === 'xui' || protocol === 'xui') {
|
||||
return { protocol: 'xui', server_id: 0 };
|
||||
}
|
||||
return { protocol: raw, server_id: 0 };
|
||||
return { protocol, server_id: parseInt(serverVal || '0', 10) || 0 };
|
||||
}
|
||||
|
||||
function setProtocolSelect(protocol, serverId) {
|
||||
const sel = document.getElementById('inviteProtocol');
|
||||
if (protocol === 'xui') sel.value = 'xui';
|
||||
else {
|
||||
const want = `${protocol}|${serverId || 0}`;
|
||||
if ([...sel.options].some(o => o.value === want)) sel.value = want;
|
||||
else sel.value = 'xui';
|
||||
function setInviteSelection(protocol, serverId) {
|
||||
const serverSel = document.getElementById('inviteServer');
|
||||
if (protocol === 'xui') {
|
||||
if ([...serverSel.options].some(o => o.value === 'xui')) serverSel.value = 'xui';
|
||||
fillInviteProtocols('xui');
|
||||
return;
|
||||
}
|
||||
updateInviteProtoFields();
|
||||
const sid = String(serverId || 0);
|
||||
if ([...serverSel.options].some(o => o.value === sid)) serverSel.value = sid;
|
||||
else if (serverSel.options.length) serverSel.selectedIndex = xuiConfigured ? 1 : 0;
|
||||
fillInviteProtocols(protocol);
|
||||
}
|
||||
|
||||
function openCreateInvite() {
|
||||
@@ -269,12 +345,17 @@
|
||||
document.getElementById('inviteNote').value = '';
|
||||
document.getElementById('inviteClearPwdWrap').style.display = 'none';
|
||||
document.getElementById('inviteResetUsedWrap').style.display = 'none';
|
||||
setProtocolSelect('xui', 0);
|
||||
const serverSel = document.getElementById('inviteServer');
|
||||
if (xuiConfigured && [...serverSel.options].some(o => o.value === 'xui')) serverSel.value = 'xui';
|
||||
else if (serverSel.options.length) serverSel.selectedIndex = 0;
|
||||
fillInviteProtocols();
|
||||
if (xuiDefaultPanelId) {
|
||||
const p = document.getElementById('inviteXuiPanel');
|
||||
if (p && [...p.options].some(o => o.value === xuiDefaultPanelId)) p.value = xuiDefaultPanelId;
|
||||
}
|
||||
loadInviteInbounds(xuiDefaultInbound);
|
||||
if (document.getElementById('inviteProtocol').value === 'xui') {
|
||||
loadInviteInbounds(xuiDefaultInbound);
|
||||
}
|
||||
openModal('inviteModal');
|
||||
}
|
||||
|
||||
@@ -293,12 +374,14 @@
|
||||
document.getElementById('inviteClearPassword').checked = false;
|
||||
document.getElementById('inviteResetUsedWrap').style.display = 'block';
|
||||
document.getElementById('inviteResetUsed').checked = false;
|
||||
setProtocolSelect(inv.protocol || 'xui', inv.server_id || 0);
|
||||
setInviteSelection(inv.protocol || 'xui', inv.server_id || 0);
|
||||
const panelSel = document.getElementById('inviteXuiPanel');
|
||||
if (panelSel && inv.xui_panel_id && [...panelSel.options].some(o => o.value === inv.xui_panel_id)) {
|
||||
panelSel.value = inv.xui_panel_id;
|
||||
}
|
||||
loadInviteInbounds(inv.xui_inbound_id || xuiDefaultInbound);
|
||||
if ((inv.protocol || '') === 'xui') {
|
||||
loadInviteInbounds(inv.xui_inbound_id || xuiDefaultInbound);
|
||||
}
|
||||
openModal('inviteModal');
|
||||
}
|
||||
|
||||
@@ -309,7 +392,8 @@
|
||||
spinner.classList.remove('hidden');
|
||||
try {
|
||||
const editId = document.getElementById('inviteEditId').value;
|
||||
const proto = parseProtocolValue();
|
||||
const proto = parseInviteSelection();
|
||||
if (!proto.protocol) throw new Error(_('no_protocols'));
|
||||
const inbound = parseInt(document.getElementById('inviteInboundId').value || '0');
|
||||
if (proto.protocol === 'xui' && !inbound) throw new Error(_('invite_inbound_required'));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user