@@ -294,30 +294,6 @@
< / div >
< / div >
<!-- Hysteria Card -->
< div class = "card card-hover protocol-card protocol-hysteria" id = "proto-hysteria" >
< div style = "display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);" >
< div class = "protocol-icon" > 🌀< / div >
< div class = "flex gap-sm" id = "hysteria-ctrl" style = "display:none!important;" > < / div >
< / div >
< div class = "protocol-name" > Hysteria 2< / div >
< div class = "protocol-desc" >
{{ _('hysteria_desc') }}
< / div >
< div class = "protocol-status" id = "hysteria-status" >
< span class = "badge badge-warn" > < span class = "badge-dot" > < / span > {{ _('not_checked') }}< / span >
< / div >
< div id = "hysteria-info" class = "hidden" >
< div class = "protocol-info" id = "hysteria-info-grid" > < / div >
< / div >
< div class = "flex gap-sm" id = "hysteria-actions" >
< button class = "btn btn-primary btn-sm" onclick = "openInstallModal('hysteria')" id = "hysteria-install-btn"
style = "flex:1" >
{{ _('install') }}
< / button >
< / div >
< / div >
<!-- WireGuard Card -->
< div class = "card card-hover protocol-card protocol-wireguard" id = "proto-wireguard" >
< div style = "display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);" >
@@ -679,25 +655,6 @@
< div class = "form-hint" > {{ _('nginx_install_hint') }}< / div >
< / div >
< div id = "hysteriaOptions"
style = "display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);" >
< div class = "form-group" >
< label class = "form-label" > {{ _('port') }} (UDP) *< / label >
< input class = "form-input" type = "number" id = "installHysteriaPort" value = "8998" min = "1" max = "65535" >
< div class = "form-hint" > {{ _('hysteria_port_hint') }}< / div >
< / div >
< div class = "form-group" >
< label class = "form-label" > {{ _('hysteria_domain') }}< / label >
< input class = "form-input" type = "text" id = "installHysteriaDomain" placeholder = "vpn.example.com" oninput = "updateHysteriaDnsHint()" >
< div class = "form-hint" id = "hysteriaDnsHint" > < / div >
< / div >
< div class = "form-group" >
< label class = "form-label" > {{ _('hysteria_email') }}< / label >
< input class = "form-input" type = "email" id = "installHysteriaEmail" placeholder = "admin@example.com" >
< / div >
< div class = "form-hint" > {{ _('hysteria_install_hint') }}< / div >
< / div >
< div class = "modal-footer" >
< button class = "btn btn-secondary" onclick = "closeModal('installModal')" > {{ _('cancel') }}< / button >
< button class = "btn btn-primary" onclick = "installProtocol()" id = "installBtn" >
@@ -752,33 +709,6 @@
< / div >
< / div >
<!-- ===== Hysteria Settings Modal ===== -->
< div class = "modal-backdrop" id = "hysteriaSettingsModal" >
< div class = "modal" >
< div class = "modal-header" >
< h2 class = "modal-title" id = "hysteriaSettingsTitle" > {{ _('hysteria_settings_title') }}< / h2 >
< button class = "modal-close" onclick = "closeModal('hysteriaSettingsModal')" > × < / button >
< / div >
< input type = "hidden" id = "hysteriaSetProto" value = "hysteria" / >
< div class = "form-group" >
< label class = "form-label" > {{ _('hysteria_domain') }}< / label >
< input class = "form-input" type = "text" id = "hysteriaSetDomain" disabled >
< / div >
< div class = "form-group" >
< label class = "form-label" > {{ _('port') }} (UDP)< / label >
< input class = "form-input" type = "number" id = "hysteriaSetPort" min = "1" max = "65535" value = "8998" >
< div class = "form-hint" > {{ _('hysteria_port_change_hint') }}< / div >
< / div >
< div class = "modal-footer" >
< button class = "btn btn-secondary" onclick = "closeModal('hysteriaSettingsModal')" > {{ _('cancel') }}< / button >
< button class = "btn btn-primary" onclick = "saveHysteriaSettings()" id = "hysteriaSaveBtn" >
< span id = "hysteriaSaveBtnText" > {{ _('save') }}< / span >
< div class = "spinner hidden" id = "hysteriaSaveSpinner" > < / div >
< / button >
< / div >
< / div >
< / div >
<!-- ===== NGINX Site Modal ===== -->
< div class = "modal-backdrop" id = "siteConfigModal" >
< div class = "modal" style = "max-width:680px;" >
@@ -1027,7 +957,6 @@
{ proto : 'awg_legacy' , category : 'protocols' , icon : '📡' , title : 'AmneziaWG Legacy' , descKey : 'awg_legacy_desc' } ,
{ proto : 'xray' , category : 'protocols' , icon : '⚡' , title : 'Xray (VLESS-Reality)' , descKey : 'xray_desc' } ,
{ proto : 'telemt' , category : 'protocols' , icon : '✈' , title : 'Telemt (Telegram Proxy)' , descKey : 'telemt_desc' } ,
{ proto : 'hysteria' , category : 'protocols' , icon : '🌀' , title : 'Hysteria 2' , descKey : 'hysteria_desc' } ,
{ proto : 'wireguard' , category : 'protocols' , icon : '🔒' , title : 'WireGuard' , descKey : 'wireguard_desc' } ,
{ proto : 'dns' , category : 'services' , icon : '🔍' , title : 'AmneziaDNS' , descKey : 'dns_desc' } ,
{ proto : 'adguard' , category : 'services' , icon : '🛡️' , title : 'AdGuard Home' , descKey : 'adguard_desc' } ,
@@ -1063,7 +992,7 @@
}
function isMultiInstanceBase ( proto ) {
return [ 'awg' , 'awg2' , 'awg_legacy' , 'xray' , 'telemt' , 'hysteria' , 'socks5' ] . includes ( protoBase ( proto ) ) ;
return [ 'awg' , 'awg2' , 'awg_legacy' , 'xray' , 'telemt' , 'socks5' ] . includes ( protoBase ( proto ) ) ;
}
function nextSuggestedPort ( base , fallback ) {
@@ -1268,7 +1197,6 @@
case 'awg_legacy' : title = 'AmneziaWG Legacy' ; break ;
case 'xray' : title = 'Xray' ; break ;
case 'telemt' : title = 'Telemt' ; break ;
case 'hysteria' : title = 'Hysteria 2' ; break ;
case 'wireguard' : title = 'WireGuard' ; break ;
case 'dns' : title = 'AmneziaDNS' ; break ;
case 'socks5' : title = 'SOCKS5 Proxy' ; break ;
@@ -1328,7 +1256,7 @@
for ( const [ proto , info ] of orderedProtocolEntries ( data . protocols ) ) {
updateProtocolCard ( proto , info ) ;
const isVPN = [ 'awg' , 'awg2' , 'awg_legacy' , 'xray' , 'telemt' , 'hysteria' , 'wireguard' ] . includes ( protoBase ( proto ) ) ;
const isVPN = [ 'awg' , 'awg2' , 'awg_legacy' , 'xray' , 'telemt' , 'wireguard' ] . includes ( protoBase ( proto ) ) ;
if ( info . container _running && isVPN ) {
const opt = document . createElement ( 'option' ) ;
opt . value = proto ;
@@ -1557,9 +1485,6 @@
grid = buildServiceInfoGrid ( proto , info ) ;
} else if ( info . port ) {
grid += ` <div class="protocol-info-item"><span class="protocol-info-label"> ${ _ ( 'port' ) } </span><span class="protocol-info-value"> ${ info . port } / ${ ( [ 'xray' , 'telemt' ] . includes ( protoBase ( proto ) ) ) ? 'TCP' : 'UDP' } </span></div> ` ;
if ( protoBase ( proto ) === 'hysteria' && info . domain ) {
grid += ` <div class="protocol-info-item"><span class="protocol-info-label"> ${ _ ( 'hysteria_domain' ) } </span><span class="protocol-info-value"> ${ info . domain } </span></div> ` ;
}
}
if ( ! isService && info . clients _count !== undefined ) {
grid += ` <div class="protocol-info-item"><span class="protocol-info-label"> ${ _ ( 'connections' ) } </span><span class="protocol-info-value"> ${ info . clients _count } </span></div> ` ;
@@ -1594,14 +1519,6 @@
<button class="btn btn-secondary btn-sm" onclick="showProtocolBackups(' ${ proto } ')" style="flex:1"> ${ _ ( 'backup' ) } </button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol(' ${ proto } ')"> ${ _ ( 'uninstall' ) } </button>
` ;
} else if ( protoBase ( proto ) === 'hysteria' ) {
actionsEl . innerHTML = `
<button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns(' ${ proto } ')" style="flex:1"> ${ _ ( 'connections' ) } </button>
<button class="btn btn-secondary btn-sm" onclick="openHysteriaSettings(' ${ proto } ')"> ${ _ ( 'hysteria_change_port' ) } </button>
<button class="btn btn-secondary btn-sm" onclick="showProtocolBackups(' ${ proto } ')"> ${ _ ( 'backup' ) } </button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol(' ${ proto } ')"> ${ _ ( 'uninstall' ) } </button>
` ;
showConnectionsSection ( ) ;
} else {
actionsEl . innerHTML = `
<button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns(' ${ proto } ')" style="flex:1"> ${ _ ( 'connections' ) } </button>
@@ -1638,7 +1555,6 @@
} else if ( info . container _exists ) {
installedProtocols [ proto ] = true ;
const errText = String ( info . error || '' ) . trim ( ) ;
const portBusy = ! ! info . port _busy || /address already in use|UDP port \d+ already in use|already in use/i . test ( errText ) ;
statusEl . innerHTML = errText
? ` <span class="badge badge-danger" style="max-width:100%; white-space:normal; text-align:left; line-height:1.35;"><span class="badge-dot"></span> ${ _ ( 'stop' ) } : ${ escapeHtml ( errText . slice ( 0 , 120 ) ) } </span> `
: ` <span class="badge badge-danger"><span class="badge-dot"></span> ${ _ ( 'stop' ) } </span> ` ;
@@ -1657,26 +1573,6 @@
if ( errText ) {
grid += ` <div class="protocol-info-item" style="grid-column:1/-1;"><span class="protocol-info-label"> ${ _ ( 'error' ) } </span><span class="protocol-info-value" style="color:var(--danger); word-break:break-word;"> ${ escapeHtml ( errText ) } </span></div> ` ;
}
// Inline port picker — always visible when Hysteria is stopped so admin can fix bind conflicts
if ( protoBase ( proto ) === 'hysteria' ) {
const curPort = parseInt ( info . port , 10 ) || 443 ;
const suggest = ( curPort === 443 || portBusy ) ? 8998 : curPort ;
grid += `
<div class="protocol-info-item" style="grid-column:1/-1; display:block; padding-top:8px;">
<span class="protocol-info-label" style="display:block; margin-bottom:6px;"> ${ _ ( 'hysteria_change_port' ) } (UDP)</span>
<div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
<input class="form-input" type="number" id="hysteriaInlinePort- ${ protoDomKey ( proto ) } "
value=" ${ suggest } " min="1" max="65535" style="width:120px; flex:0 0 auto;">
<button class="btn btn-primary btn-sm" type="button"
onclick="applyHysteriaPort(' ${ proto } ', document.getElementById('hysteriaInlinePort- ${ protoDomKey ( proto ) } ').value)">
${ _ ( 'save' ) } & ${ _ ( 'start_btn' ) }
</button>
<button class="btn btn-secondary btn-sm" type="button" onclick="openHysteriaSettings(' ${ proto } ')"> ${ _ ( 'hysteria_settings_title' ) } </button>
</div>
<div class="form-hint" style="margin-top:6px;"> ${ _ ( 'hysteria_port_change_hint' ) } </div>
</div>
` ;
}
infoGrid . innerHTML = grid ;
}
if ( isService ) {
@@ -1686,13 +1582,6 @@
<button class="btn btn-secondary btn-sm" onclick="showProtocolBackups(' ${ proto } ')"> ${ _ ( 'backup' ) } </button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol(' ${ proto } ')"> ${ _ ( 'uninstall' ) } </button>
` ;
} else if ( protoBase ( proto ) === 'hysteria' ) {
actionsEl . innerHTML = `
<button class="btn btn-primary btn-sm" onclick="openHysteriaSettings(' ${ proto } ')" style="flex:1"> ${ _ ( 'hysteria_change_port' ) } </button>
<button class="btn btn-secondary btn-sm" onclick="showContainerLogs(' ${ proto } ')"> ${ _ ( 'logs_btn' ) } </button>
<button class="btn btn-secondary btn-sm" onclick="openInstallModal(' ${ proto } ')"> ${ _ ( 'reinstall' ) } </button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol(' ${ proto } ')"> ${ _ ( 'uninstall' ) } </button>
` ;
} else {
actionsEl . innerHTML = `
<button class="btn btn-primary btn-sm" onclick="openInstallModal(' ${ proto } ')" style="flex:1"> ${ _ ( 'reinstall' ) } </button>
@@ -1973,14 +1862,6 @@
hint . innerHTML = ` ${ _ ( 'nginx_dns_hint' ) } <code>A ${ domain } ${ SERVER _HOST } </code> ` ;
}
function updateHysteriaDnsHint ( ) {
const input = document . getElementById ( 'installHysteriaDomain' ) ;
const hint = document . getElementById ( 'hysteriaDnsHint' ) ;
if ( ! input || ! hint ) return ;
const domain = ( input . value || '' ) . trim ( ) || 'vpn.example.com' ;
hint . innerHTML = ` ${ _ ( 'hysteria_dns_hint' ) } <code>A ${ domain } ${ SERVER _HOST } </code> ` ;
}
function openInstallModal ( proto , installAnother = false ) {
const base = protoBase ( proto ) ;
currentInstallProto = installAnother ? base : proto ;
@@ -1996,13 +1877,11 @@
const socks5Opts = document . getElementById ( 'socks5Options' ) ;
const adguardOpts = document . getElementById ( 'adguardOptions' ) ;
const nginxOpts = document . getElementById ( 'nginxOptions' ) ;
const hysteriaOpts = document . getElementById ( 'hysteriaOptions' ) ;
telemtOpts . style . display = 'none' ;
socks5Opts . style . display = 'none' ;
adguardOpts . style . display = 'none' ;
nginxOpts . style . display = 'none' ;
hysteriaOpts . style . display = 'none' ;
if ( portGroup ) portGroup . style . display = '' ;
if ( base === 'dns' ) {
@@ -2040,19 +1919,6 @@
portHint . textContent = _ ( 'nginx_port_hint' ) ;
nginxOpts . style . display = 'block' ;
updateNginxDnsHint ( ) ;
} else if ( base === 'hysteria' ) {
// Port is chosen inside hysteriaOptions (dedicated UDP field)
if ( portGroup ) portGroup . style . display = 'none' ;
const suggested = currentInstallAnother ? nextSuggestedPort ( currentInstallProto , 8998 ) : '8998' ;
portInput . value = suggested ;
portInput . disabled = false ;
hysteriaOpts . style . display = 'block' ;
const hyPort = document . getElementById ( 'installHysteriaPort' ) ;
if ( hyPort ) {
hyPort . value = suggested ;
hyPort . oninput = ( ) => { portInput . value = hyPort . value ; } ;
}
updateHysteriaDnsHint ( ) ;
} else {
portLabel . textContent = _ ( 'port' ) + ' (UDP)' ;
portInput . value = currentInstallAnother ? nextSuggestedPort ( currentInstallProto , 55424 ) : '55424' ;
@@ -2119,13 +1985,6 @@
} else if ( protoBase ( currentInstallProto ) === 'nginx' ) {
params . nginx _domain = document . getElementById ( 'installNginxDomain' ) . value . trim ( ) ;
params . nginx _email = document . getElementById ( 'installNginxEmail' ) . value . trim ( ) ;
} else if ( protoBase ( currentInstallProto ) === 'hysteria' ) {
const hyPortEl = document . getElementById ( 'installHysteriaPort' ) ;
if ( hyPortEl && hyPortEl . value ) {
params . port = hyPortEl . value ;
}
params . hysteria _domain = document . getElementById ( 'installHysteriaDomain' ) . value . trim ( ) ;
params . hysteria _email = document . getElementById ( 'installHysteriaEmail' ) . value . trim ( ) ;
}
const result = await apiCall ( ` /api/servers/ ${ SERVER _ID } /install ` , 'POST' , params ) ;
clearInterval ( progressInterval ) ;
@@ -2299,84 +2158,6 @@
}
}
// ========== Hysteria Settings (port) ==========
async function applyHysteriaPort ( proto , portValue ) {
const port = parseInt ( portValue , 10 ) ;
if ( ! port || port < 1 || port > 65535 ) {
showToast ( _ ( 'error' ) + ': invalid port' , 'error' ) ;
return ;
}
if ( port === 80 ) {
showToast ( _ ( 'error' ) + ': port 80 reserved' , 'error' ) ;
return ;
}
try {
showToast ( _ ( 'saving' ) || 'Saving...' , 'info' ) ;
const res = await apiCall ( ` /api/servers/ ${ SERVER _ID } /hysteria/settings ` , 'POST' , {
protocol : proto || 'hysteria' , port ,
} ) ;
showToast ( res . message || _ ( 'hysteria_settings_saved' ) , 'success' ) ;
setTimeout ( ( ) => checkServer ( ) , 800 ) ;
} catch ( err ) {
showToast ( _ ( 'error' ) + ': ' + err . message , 'error' ) ;
// Still open settings so admin can try another port
openHysteriaSettings ( proto ) ;
}
}
async function openHysteriaSettings ( proto = 'hysteria' ) {
const live = currentProtocolStatus [ proto ] || { } ;
const livePort = live . port || 443 ;
const suggest = ( parseInt ( livePort , 10 ) === 443 ) ? 8998 : ( livePort || 8998 ) ;
document . getElementById ( 'hysteriaSetProto' ) . value = proto ;
document . getElementById ( 'hysteriaSettingsTitle' ) . textContent =
` ${ _ ( 'hysteria_settings_title' ) } — ${ getProtoTitle ( proto ) } ` ;
document . getElementById ( 'hysteriaSetDomain' ) . value = live . domain || '' ;
document . getElementById ( 'hysteriaSetPort' ) . value = suggest ;
openModal ( 'hysteriaSettingsModal' ) ;
// Enrich from server when possible (don't block UI if SSH is slow/fails)
try {
const data = await apiCall ( ` /api/servers/ ${ SERVER _ID } /hysteria/settings?protocol= ${ encodeURIComponent ( proto ) } ` ) ;
if ( data . domain ) document . getElementById ( 'hysteriaSetDomain' ) . value = data . domain ;
const p = parseInt ( data . port , 10 ) ;
if ( p && p !== 443 ) document . getElementById ( 'hysteriaSetPort' ) . value = p ;
else if ( p === 443 ) document . getElementById ( 'hysteriaSetPort' ) . value = 8998 ;
} catch ( _ ) { /* modal already open with local values */ }
}
async function saveHysteriaSettings ( ) {
const btn = document . getElementById ( 'hysteriaSaveBtn' ) ;
const text = document . getElementById ( 'hysteriaSaveBtnText' ) ;
const spinner = document . getElementById ( 'hysteriaSaveSpinner' ) ;
const proto = document . getElementById ( 'hysteriaSetProto' ) . value || 'hysteria' ;
const port = parseInt ( document . getElementById ( 'hysteriaSetPort' ) . value , 10 ) ;
if ( ! port || port < 1 || port > 65535 ) {
showToast ( _ ( 'error' ) + ': invalid port' , 'error' ) ;
return ;
}
if ( port === 80 ) {
showToast ( _ ( 'error' ) + ': port 80 reserved' , 'error' ) ;
return ;
}
btn . disabled = true ;
text . textContent = _ ( 'saving' ) || 'Saving...' ;
spinner . classList . remove ( 'hidden' ) ;
try {
const res = await apiCall ( ` /api/servers/ ${ SERVER _ID } /hysteria/settings ` , 'POST' , {
protocol : proto , port ,
} ) ;
showToast ( res . message || _ ( 'hysteria_settings_saved' ) , 'success' ) ;
closeModal ( 'hysteriaSettingsModal' ) ;
setTimeout ( ( ) => checkServer ( ) , 800 ) ;
} catch ( err ) {
showToast ( _ ( 'error' ) + ': ' + err . message , 'error' ) ;
} finally {
btn . disabled = false ;
text . textContent = _ ( 'save' ) ;
spinner . classList . add ( 'hidden' ) ;
}
}
// ========== Connection Management ==========
function selectProtocolForConns ( proto ) {
document . getElementById ( 'connectionsSection' ) . style . display = '' ;
@@ -2417,7 +2198,7 @@
const sent = userData . dataSent || '' ;
const initial = name . charAt ( 0 ) . toUpperCase ( ) ;
const enabled = ( client . enabled !== undefined ) ? client . enabled : ( userData . enabled !== false ) ;
const hasPrivKey = ! ! userData . clientPrivateKey || proto === 'xray' || proto === 'telemt' || protoBase ( proto ) === 'hysteria' ;
const hasPrivKey = ! ! userData . clientPrivateKey || proto === 'xray' || proto === 'telemt' ;
const assignedUser = client . assigned _user || '' ;
let metaHtml = '' ;
@@ -2455,7 +2236,7 @@
</div>
</div>
<div class="client-actions">
<button class="btn btn-secondary btn-sm btn-icon" onclick="showConnectionConfig(' ${ escapeJs ( client . clientId ) } ', ' ${ escapeJs ( name ) } ', ${ ! ! userData . clientPrivateKey || proto === 'xray' || proto === 'telemt' || protoBase ( proto ) === 'hysteria' } )" title=" ${ _ ( 'config' ) } ">📄</button>
<button class="btn btn-secondary btn-sm btn-icon" onclick="showConnectionConfig(' ${ escapeJs ( client . clientId ) } ', ' ${ escapeJs ( name ) } ', ${ ! ! userData . clientPrivateKey || proto === 'xray' || proto === 'telemt' } )" title=" ${ _ ( 'config' ) } ">📄</button>
${ proto === 'telemt' ? ` <button class="btn btn-secondary btn-sm btn-icon" onclick="editConnection(' ${ escapeJs ( client . clientId ) } ')" title=" ${ _ ( 'edit' ) } ">✏️</button> ` : '' }
<button class="btn btn-secondary btn-sm btn-icon" onclick="toggleConnection(' ${ escapeJs ( client . clientId ) } ', ${ ! enabled } )" title=" ${ toggleTitle } "> ${ toggleIcon } </button>
<button class="btn btn-danger btn-sm btn-icon" onclick="removeConnection(' ${ escapeJs ( client . clientId ) } ')" title=" ${ _ ( 'delete' ) } ">🗑</button>
@@ -2507,8 +2288,8 @@
document . getElementById ( 'configModalTitle' ) . textContent = ` ${ _ ( 'config' ) } — ${ connName } ` ;
document . getElementById ( 'configText' ) . textContent = result . config ;
document . getElementById ( 'vpnLinkText' ) . textContent = currentVpnLink ;
document . getElementById ( 'panel-vpn' ) . style . display = ( proto === 'telemt' || protoBase ( proto ) === 'hysteria' ? 'none' : '' ) ;
document . querySelectorAll ( '.config-tab' ) [ 1 ] . style . display = ( proto === 'telemt' || protoBase ( proto ) === 'hysteria' ? 'none' : '' ) ;
document . getElementById ( 'panel-vpn' ) . style . display = ( proto === 'telemt' ? 'none' : '' ) ;
document . querySelectorAll ( '.config-tab' ) [ 1 ] . style . display = ( proto === 'telemt' ? 'none' : '' ) ;
switchConfigTab ( 'conf' ) ;
openModal ( 'configModal' ) ;
generateQR ( result . config ) ;
@@ -2586,9 +2367,9 @@
const proto = document . getElementById ( 'connProtoSelect' ) . value ;
// Restore tabs visibility first
document . getElementById ( 'panel-vpn' ) . style . display = ( proto === 'telemt' || protoBase ( proto ) === 'hysteria' ? 'none' : '' ) ;
document . getElementById ( 'panel-vpn' ) . style . display = ( proto === 'telemt' ? 'none' : '' ) ;
document . getElementById ( 'panel-qr' ) . style . display = '' ;
document . querySelectorAll ( '.config-tab' ) [ 1 ] . style . display = ( proto === 'telemt' || protoBase ( proto ) === 'hysteria' ? 'none' : '' ) ;
document . querySelectorAll ( '.config-tab' ) [ 1 ] . style . display = ( proto === 'telemt' ? 'none' : '' ) ;
document . querySelectorAll ( '.config-tab' ) [ 2 ] . style . display = '' ;
// Client was created via native Amnezia app — private key is not stored server-side