|
|
|
@@ -363,6 +363,31 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Mieru Card -->
|
|
|
|
|
<div class="card card-hover protocol-card protocol-mieru" id="proto-mieru">
|
|
|
|
|
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
|
|
|
|
|
<div class="protocol-icon">{{ icon('zap') }}</div>
|
|
|
|
|
<div class="flex gap-sm" id="mieru-ctrl" style="display:none!important;"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="protocol-name">Mieru <span
|
|
|
|
|
style="font-size:0.65rem; background:var(--accent, #6366f1); color:#fff; padding:2px 6px; border-radius:8px; vertical-align:middle;">v3.28.0</span></div>
|
|
|
|
|
<div class="protocol-desc">
|
|
|
|
|
{{ _('mieru_desc') }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="protocol-status" id="mieru-status">
|
|
|
|
|
<span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div id="mieru-info" class="hidden">
|
|
|
|
|
<div class="protocol-info" id="mieru-info-grid"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex gap-sm" id="mieru-actions">
|
|
|
|
|
<button class="btn btn-primary btn-sm" onclick="openInstallModal('mieru')" id="mieru-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);">
|
|
|
|
@@ -777,6 +802,20 @@
|
|
|
|
|
<div class="form-hint" style="margin-top: var(--space-sm); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35);">{{ _('naiveproxy_client_hint') }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="mieruOptions"
|
|
|
|
|
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-hint" style="margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35); color: var(--text);">
|
|
|
|
|
{{ _('mieru_ports_warning') }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label class="form-label">{{ _('port') }} (TCP) *</label>
|
|
|
|
|
<input class="form-input" type="number" id="installMieruPort" value="2999" min="1025" max="65535">
|
|
|
|
|
<div class="form-hint">{{ _('mieru_port_hint') }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-hint">{{ _('mieru_install_hint') }}</div>
|
|
|
|
|
<div class="form-hint" style="margin-top: var(--space-sm); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35);">{{ _('mieru_client_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">
|
|
|
|
@@ -1162,6 +1201,7 @@
|
|
|
|
|
{ proto: 'telemt', category: 'protocols', icon: 'plane', title: 'Telemt (Telegram Proxy)', descKey: 'telemt_desc' },
|
|
|
|
|
{ proto: 'hysteria', category: 'protocols', icon: 'refresh', title: 'Hysteria 2', descKey: 'hysteria_desc' },
|
|
|
|
|
{ proto: 'naiveproxy', category: 'protocols', icon: 'link', title: 'NaiveProxy', descKey: 'naiveproxy_desc', badge: 'STABLE' },
|
|
|
|
|
{ proto: 'mieru', category: 'protocols', icon: 'zap', title: 'Mieru', descKey: 'mieru_desc', badge: 'v3.28.0' },
|
|
|
|
|
{ proto: 'wireguard', category: 'protocols', icon: 'lock', title: 'WireGuard', descKey: 'wireguard_desc' },
|
|
|
|
|
{ proto: 'dns', category: 'services', icon: 'search', title: 'AmneziaDNS', descKey: 'dns_desc' },
|
|
|
|
|
{ proto: 'adguard', category: 'services', icon: 'shield-check', title: 'AdGuard Home', descKey: 'adguard_desc' },
|
|
|
|
@@ -1495,6 +1535,7 @@
|
|
|
|
|
case 'telemt': title = 'Telemt'; break;
|
|
|
|
|
case 'hysteria': title = 'Hysteria 2'; break;
|
|
|
|
|
case 'naiveproxy': title = 'NaiveProxy'; break;
|
|
|
|
|
case 'mieru': title = 'Mieru'; break;
|
|
|
|
|
case 'wireguard': title = 'WireGuard'; break;
|
|
|
|
|
case 'dns': title = 'AmneziaDNS'; break;
|
|
|
|
|
case 'socks5': title = 'SOCKS5 Proxy'; break;
|
|
|
|
@@ -1554,7 +1595,7 @@
|
|
|
|
|
|
|
|
|
|
for (const [proto, info] of orderedProtocolEntries(data.protocols)) {
|
|
|
|
|
updateProtocolCard(proto, info);
|
|
|
|
|
const isVPN = ['awg', 'awg2', 'awg_legacy', 'xray', 'telemt', 'hysteria', 'naiveproxy', 'wireguard'].includes(protoBase(proto));
|
|
|
|
|
const isVPN = ['awg', 'awg2', 'awg_legacy', 'xray', 'telemt', 'hysteria', 'naiveproxy', 'mieru', 'wireguard'].includes(protoBase(proto));
|
|
|
|
|
if (info.container_running && isVPN) {
|
|
|
|
|
const opt = document.createElement('option');
|
|
|
|
|
opt.value = proto;
|
|
|
|
@@ -1782,7 +1823,7 @@
|
|
|
|
|
if (isService) {
|
|
|
|
|
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', 'naiveproxy'].includes(protoBase(proto))) ? 'TCP' : 'UDP'}</span></div>`;
|
|
|
|
|
grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('port')}</span><span class="protocol-info-value">${info.port}/${(['xray', 'telemt', 'naiveproxy', 'mieru'].includes(protoBase(proto))) ? 'TCP' : 'UDP'}</span></div>`;
|
|
|
|
|
if (WG_AWG_BASES.has(protoBase(proto))) {
|
|
|
|
|
const endpoint = (connectDomainEffective.effective_host && connectDomainEffective.effective_host !== SERVER_HOST)
|
|
|
|
|
? connectDomainEffective.effective_host
|
|
|
|
@@ -1795,6 +1836,9 @@
|
|
|
|
|
if (protoBase(proto) === 'naiveproxy' && info.domain) {
|
|
|
|
|
grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('naiveproxy_domain')}</span><span class="protocol-info-value">${info.domain}</span></div>`;
|
|
|
|
|
}
|
|
|
|
|
if (protoBase(proto) === 'mieru' && info.release) {
|
|
|
|
|
grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('mieru_version')}</span><span class="protocol-info-value">v${info.release}</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>`;
|
|
|
|
@@ -1844,6 +1888,13 @@
|
|
|
|
|
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
|
|
|
|
|
`;
|
|
|
|
|
showConnectionsSection();
|
|
|
|
|
} else if (protoBase(proto) === 'mieru') {
|
|
|
|
|
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="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>
|
|
|
|
@@ -2247,6 +2298,7 @@
|
|
|
|
|
const nginxOpts = document.getElementById('nginxOptions');
|
|
|
|
|
const hysteriaOpts = document.getElementById('hysteriaOptions');
|
|
|
|
|
const naiveproxyOpts = document.getElementById('naiveproxyOptions');
|
|
|
|
|
const mieruOpts = document.getElementById('mieruOptions');
|
|
|
|
|
|
|
|
|
|
telemtOpts.style.display = 'none';
|
|
|
|
|
socks5Opts.style.display = 'none';
|
|
|
|
@@ -2254,6 +2306,7 @@
|
|
|
|
|
nginxOpts.style.display = 'none';
|
|
|
|
|
hysteriaOpts.style.display = 'none';
|
|
|
|
|
if (naiveproxyOpts) naiveproxyOpts.style.display = 'none';
|
|
|
|
|
if (mieruOpts) mieruOpts.style.display = 'none';
|
|
|
|
|
if (portGroup) portGroup.style.display = '';
|
|
|
|
|
|
|
|
|
|
if (base === 'dns') {
|
|
|
|
@@ -2321,6 +2374,17 @@
|
|
|
|
|
if (npDomain && !npDomain.value && SERVER_SSL_DOMAIN) npDomain.value = SERVER_SSL_DOMAIN;
|
|
|
|
|
if (npEmail && !npEmail.value && SERVER_SSL_EMAIL) npEmail.value = SERVER_SSL_EMAIL;
|
|
|
|
|
updateNaiveproxyDnsHint();
|
|
|
|
|
} else if (base === 'mieru') {
|
|
|
|
|
if (portGroup) portGroup.style.display = 'none';
|
|
|
|
|
const suggested = '2999';
|
|
|
|
|
portInput.value = suggested;
|
|
|
|
|
portInput.disabled = false;
|
|
|
|
|
if (mieruOpts) mieruOpts.style.display = 'block';
|
|
|
|
|
const miPort = document.getElementById('installMieruPort');
|
|
|
|
|
if (miPort) {
|
|
|
|
|
miPort.value = suggested;
|
|
|
|
|
miPort.oninput = () => { portInput.value = miPort.value; };
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
portLabel.textContent = _('port') + ' (UDP)';
|
|
|
|
|
portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 55424) : '55424';
|
|
|
|
@@ -2398,6 +2462,11 @@
|
|
|
|
|
params.port = '443';
|
|
|
|
|
params.naiveproxy_domain = document.getElementById('installNaiveproxyDomain').value.trim();
|
|
|
|
|
params.naiveproxy_email = document.getElementById('installNaiveproxyEmail').value.trim();
|
|
|
|
|
} else if (protoBase(currentInstallProto) === 'mieru') {
|
|
|
|
|
const miPortEl = document.getElementById('installMieruPort');
|
|
|
|
|
if (miPortEl && miPortEl.value) {
|
|
|
|
|
params.port = miPortEl.value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const result = await apiCall(`/api/servers/${SERVER_ID}/install`, 'POST', params);
|
|
|
|
|
clearInterval(progressInterval);
|
|
|
|
|