diff --git a/app.py b/app.py
index 39c08dc..709d290 100644
--- a/app.py
+++ b/app.py
@@ -103,7 +103,7 @@ else:
application_path = os.path.dirname(__file__)
DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export
-CURRENT_VERSION = "v3.1.1"
+CURRENT_VERSION = "v3.1.2"
RELEASES_REPO_URL = repo_url()
RELEASES_API_LATEST = api_latest_url()
BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin'))
diff --git a/managers/xray_manager.py b/managers/xray_manager.py
index 550eb1a..a98b6b3 100644
--- a/managers/xray_manager.py
+++ b/managers/xray_manager.py
@@ -26,7 +26,7 @@ class XrayManager:
PROTOCOL = 'xray'
CONTAINER_NAME = 'amnezia-xray'
IMAGE_NAME = 'amneziavpn/amnezia-xray'
- DEFAULT_PORT = 443
+ DEFAULT_PORT = 8443
def __init__(self, ssh_manager, protocol='xray'):
self.ssh = ssh_manager
@@ -298,13 +298,15 @@ chmod 644 {_q(self._cert_path())} {_q(self._key_path())}
stream = (inbound or {}).get('streamSettings') or {}
return str(stream.get('security') or '').lower() == 'reality'
- def install_protocol(self, port=443, domain=None, email=None, site_name=None,
+ def install_protocol(self, port=8443, domain=None, email=None, site_name=None,
acme_method='cloudflare', cloudflare_token=None):
"""Install VLESS + XHTTP + TLS (Let's Encrypt via Cloudflare DNS or HTTP-01)."""
results = []
domain = self._validate_domain(domain or site_name)
email = self._validate_email(email)
port = int(port or self.DEFAULT_PORT)
+ if port < 1 or port > 65535:
+ raise ValueError('Port must be between 1 and 65535')
method = (acme_method or 'cloudflare').strip().lower()
if method in ('cf', 'dns', 'dns-01', 'cloudflare_dns'):
method = 'cloudflare'
diff --git a/templates/server.html b/templates/server.html
index b10a181..1e27572 100644
--- a/templates/server.html
+++ b/templates/server.html
@@ -801,6 +801,21 @@
{{ _('xray_ports_warning_cf') }}
+
+
+
+
+
+
+
+
{{ _('xray_port_hint') }}
+
@@ -2444,6 +2459,47 @@
return el ? el.value : 'cloudflare';
}
+ function getXrayPortMode() {
+ const el = document.querySelector('input[name="xrayPortMode"]:checked');
+ return el ? el.value : 'custom';
+ }
+
+ function syncXrayPortToInstall() {
+ const portInput = document.getElementById('installPort');
+ const xrPort = document.getElementById('installXrayPort');
+ if (!portInput || !xrPort) return;
+ if (getXrayPortMode() === 'standard') {
+ portInput.value = '443';
+ xrPort.value = '443';
+ } else {
+ const v = parseInt(xrPort.value, 10);
+ portInput.value = (v >= 1 && v <= 65535) ? String(v) : '8443';
+ }
+ }
+
+ function updateXrayPortUi() {
+ const mode = getXrayPortMode();
+ const xrPort = document.getElementById('installXrayPort');
+ const hint = document.getElementById('xrayPortHint');
+ if (xrPort) {
+ if (mode === 'standard') {
+ xrPort.value = '443';
+ xrPort.disabled = true;
+ } else {
+ xrPort.disabled = false;
+ if (!xrPort.value || xrPort.value === '443') {
+ xrPort.value = currentInstallAnother
+ ? String(nextSuggestedPort(currentInstallProto, 8443))
+ : '8443';
+ }
+ }
+ }
+ if (hint) {
+ hint.textContent = mode === 'standard' ? _('xray_port_hint_standard') : _('xray_port_hint');
+ }
+ syncXrayPortToInstall();
+ }
+
function updateXrayAcmeUi() {
const method = getXrayAcmeMethod();
const cfGroup = document.getElementById('xrayCfTokenGroup');
@@ -2451,10 +2507,6 @@
if (cfGroup) cfGroup.style.display = method === 'cloudflare' ? '' : 'none';
if (warn) warn.textContent = method === 'cloudflare' ? _('xray_ports_warning_cf') : _('xray_ports_warning');
updateXrayDnsHint();
- const portHint = document.getElementById('installPortHint');
- if (portHint && protoBase(currentInstallProto) === 'xray' && !currentInstallAnother) {
- portHint.textContent = method === 'cloudflare' ? _('port_xray_hint_cf') : _('port_xray_hint');
- }
}
function updateXrayDnsHint() {
@@ -2503,15 +2555,18 @@
portInput.disabled = true;
portHint.textContent = _('dns_internal_hint');
} else if (base === 'xray') {
- portLabel.textContent = _('port') + ' (TCP)';
+ if (portGroup) portGroup.style.display = 'none';
portInput.disabled = false;
- portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 443) : '443';
- portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('port_xray_hint');
if (xrayOpts) xrayOpts.style.display = 'block';
const xrDomain = document.getElementById('installXrayDomain');
const xrEmail = document.getElementById('installXrayEmail');
if (xrDomain && !xrDomain.value && SERVER_SSL_DOMAIN) xrDomain.value = SERVER_SSL_DOMAIN;
if (xrEmail && !xrEmail.value && SERVER_SSL_EMAIL) xrEmail.value = SERVER_SSL_EMAIL;
+ const stdRadio = document.querySelector('input[name="xrayPortMode"][value="standard"]');
+ const customRadio = document.querySelector('input[name="xrayPortMode"][value="custom"]');
+ if (customRadio) customRadio.checked = true;
+ if (stdRadio) stdRadio.checked = false;
+ updateXrayPortUi();
updateXrayAcmeUi();
} else if (base === 'telemt') {
portLabel.textContent = _('port') + ' (TCP)';
@@ -2599,10 +2654,16 @@
const xrEmail = (document.getElementById('installXrayEmail')?.value || '').trim();
const acme = getXrayAcmeMethod();
const cfToken = (document.getElementById('installXrayCfToken')?.value || '').trim();
+ syncXrayPortToInstall();
+ const xrPort = parseInt(document.getElementById('installPort')?.value, 10);
if (!xrDomain || !xrEmail) {
showToast(_('xray_domain') + ' / ' + _('xray_email'), 'error');
return;
}
+ if (!(xrPort >= 1 && xrPort <= 65535)) {
+ showToast(_('xray_listen_port'), 'error');
+ return;
+ }
if (acme === 'cloudflare' && !cfToken) {
showToast(_('xray_cf_token'), 'error');
return;
@@ -2667,6 +2728,8 @@
params.hysteria_domain = document.getElementById('installHysteriaDomain').value.trim();
params.hysteria_email = document.getElementById('installHysteriaEmail').value.trim();
} else if (protoBase(currentInstallProto) === 'xray') {
+ syncXrayPortToInstall();
+ params.port = document.getElementById('installPort').value;
params.xray_domain = (document.getElementById('installXrayDomain')?.value || '').trim();
params.xray_email = (document.getElementById('installXrayEmail')?.value || '').trim();
params.xray_acme_method = getXrayAcmeMethod();
diff --git a/translations/en.json b/translations/en.json
index 041779a..57e2c5d 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -82,15 +82,20 @@
"no_connections_desc": "Add your first connection to generate a VPN configuration",
"install_protocol": "Install protocol",
"port_default_hint": "Default port: 55424. Make sure it\u0027s not busy",
- "port_xray_hint": "Default 443/TCP. Prefer Cloudflare DNS ACME — port 80 is not needed. Point domain A-record (DNS only / grey cloud) to this server.",
- "port_xray_hint_cf": "Default 443/TCP. Certificate via Cloudflare DNS — port 80 is not used. Keep Cloudflare proxy off (grey cloud).",
+ "port_xray_hint": "Choose standard 443/TCP or any free custom TCP port. Prefer Cloudflare DNS ACME — port 80 is not needed.",
+ "port_xray_hint_cf": "Choose 443 or a custom TCP port. Certificate via Cloudflare DNS — port 80 is not used. Keep Cloudflare proxy off (grey cloud).",
"xray_domain": "Domain",
"xray_email": "Let\u0027s Encrypt email",
"xray_dns_hint": "Create DNS record:",
"xray_dns_hint_cf": "Domain must be on Cloudflare. A-record (DNS only):",
- "xray_install_hint": "Installs VLESS + XHTTP + TLS (Xray-core). Prefer Cloudflare API token (DNS-01) so TCP 80 stays free.",
- "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01). Reinstall replaces the previous Xray stack.",
- "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud). Reinstall replaces the previous Xray stack.",
+ "xray_install_hint": "Installs VLESS + XHTTP + TLS (Xray-core). Prefer Cloudflare API token (DNS-01) so TCP 80 stays free. Port 443 is optional.",
+ "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01). Listen port can be 443 or any free TCP port. Reinstall replaces the previous Xray stack.",
+ "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Listen on 443 or any free TCP port. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_listen_port": "Listen port (TCP)",
+ "xray_port_standard": "Standard — 443/TCP",
+ "xray_port_custom": "Custom port",
+ "xray_port_hint": "Any free TCP port (default 8443). Open it in the firewall. Does not occupy 443.",
+ "xray_port_hint_standard": "Uses 443/TCP. Make sure nothing else is bound to 443 on this server.",
"xray_acme_method": "Certificate method",
"xray_acme_cloudflare": "Cloudflare DNS (API token) — recommended, no port 80",
"xray_acme_http": "HTTP-01 — needs free TCP 80",
diff --git a/translations/fa.json b/translations/fa.json
index dbb059b..86d1f85 100644
--- a/translations/fa.json
+++ b/translations/fa.json
@@ -80,15 +80,20 @@
"no_connections_desc": "اولین اتصال خود را برای ایجاد پیکربندی VPN اضافه کنید",
"install_protocol": "نصب پروتکل",
"port_default_hint": "پورت پیشفرض: 55424. مطمئن شوید این پورت آزاد است.",
- "port_xray_hint": "Default 443/TCP. Prefer Cloudflare DNS ACME — port 80 is not needed.",
- "port_xray_hint_cf": "Default 443/TCP. Certificate via Cloudflare DNS — port 80 is not used.",
+ "port_xray_hint": "Choose standard 443/TCP or any free custom TCP port. Prefer Cloudflare DNS ACME — port 80 is not needed.",
+ "port_xray_hint_cf": "Choose 443 or a custom TCP port. Certificate via Cloudflare DNS — port 80 is not used.",
"xray_domain": "Domain",
"xray_email": "Let\u0027s Encrypt email",
"xray_dns_hint": "Create DNS record:",
"xray_dns_hint_cf": "Domain must be on Cloudflare. A-record (DNS only):",
- "xray_install_hint": "Installs VLESS + XHTTP + TLS. Prefer Cloudflare API token (DNS-01) so TCP 80 stays free.",
- "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01).",
- "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_install_hint": "Installs VLESS + XHTTP + TLS. Prefer Cloudflare API token (DNS-01) so TCP 80 stays free. Port 443 is optional.",
+ "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01). Listen port can be 443 or any free TCP port.",
+ "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Listen on 443 or any free TCP port. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_listen_port": "Listen port (TCP)",
+ "xray_port_standard": "Standard — 443/TCP",
+ "xray_port_custom": "Custom port",
+ "xray_port_hint": "Any free TCP port (default 8443). Open it in the firewall. Does not occupy 443.",
+ "xray_port_hint_standard": "Uses 443/TCP. Make sure nothing else is bound to 443 on this server.",
"xray_acme_method": "Certificate method",
"xray_acme_cloudflare": "Cloudflare DNS (API token) — recommended, no port 80",
"xray_acme_http": "HTTP-01 — needs free TCP 80",
diff --git a/translations/fr.json b/translations/fr.json
index d124425..398c5e5 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -80,15 +80,20 @@
"no_connections_desc": "Ajoutez votre première connexion pour générer un fichier VPN",
"install_protocol": "Installer le protocole",
"port_default_hint": "Port par défaut : 55424. Assurez-vous qu\u0027il est libre.",
- "port_xray_hint": "Default 443/TCP. Prefer Cloudflare DNS ACME — port 80 is not needed.",
- "port_xray_hint_cf": "Default 443/TCP. Certificate via Cloudflare DNS — port 80 is not used.",
+ "port_xray_hint": "Choose standard 443/TCP or any free custom TCP port. Prefer Cloudflare DNS ACME — port 80 is not needed.",
+ "port_xray_hint_cf": "Choose 443 or a custom TCP port. Certificate via Cloudflare DNS — port 80 is not used.",
"xray_domain": "Domain",
"xray_email": "Let\u0027s Encrypt email",
"xray_dns_hint": "Create DNS record:",
"xray_dns_hint_cf": "Domain must be on Cloudflare. A-record (DNS only):",
- "xray_install_hint": "Installs VLESS + XHTTP + TLS. Prefer Cloudflare API token (DNS-01) so TCP 80 stays free.",
- "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01).",
- "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_install_hint": "Installs VLESS + XHTTP + TLS. Prefer Cloudflare API token (DNS-01) so TCP 80 stays free. Port 443 is optional.",
+ "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01). Listen port can be 443 or any free TCP port.",
+ "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Listen on 443 or any free TCP port. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_listen_port": "Listen port (TCP)",
+ "xray_port_standard": "Standard — 443/TCP",
+ "xray_port_custom": "Custom port",
+ "xray_port_hint": "Any free TCP port (default 8443). Open it in the firewall. Does not occupy 443.",
+ "xray_port_hint_standard": "Uses 443/TCP. Make sure nothing else is bound to 443 on this server.",
"xray_acme_method": "Certificate method",
"xray_acme_cloudflare": "Cloudflare DNS (API token) — recommended, no port 80",
"xray_acme_http": "HTTP-01 — needs free TCP 80",
diff --git a/translations/ru.json b/translations/ru.json
index 0225cd2..52f7368 100644
--- a/translations/ru.json
+++ b/translations/ru.json
@@ -82,15 +82,20 @@
"no_connections_desc": "Добавьте первое подключение для генерации VPN конфигурации",
"install_protocol": "Установить протокол",
"port_default_hint": "Порт по умолчанию: 55424. Убедитесь, что он не занят",
- "port_xray_hint": "По умолчанию 443/TCP. Лучше Cloudflare DNS ACME — порт 80 не нужен. A-запись домена (только DNS / серое облако) на этот сервер.",
- "port_xray_hint_cf": "По умолчанию 443/TCP. Сертификат через Cloudflare DNS — порт 80 не используется. Прокси Cloudflare выключите (серое облако).",
+ "port_xray_hint": "Можно взять стандартный 443/TCP или любой свободный порт. Лучше Cloudflare DNS ACME — порт 80 не нужен.",
+ "port_xray_hint_cf": "443 или свой TCP-порт. Сертификат через Cloudflare DNS — порт 80 не используется. Прокси Cloudflare выключите (серое облако).",
"xray_domain": "Домен",
"xray_email": "Email для Let\u0027s Encrypt",
"xray_dns_hint": "Создайте DNS-запись:",
"xray_dns_hint_cf": "Домен должен быть в Cloudflare. A-запись (только DNS):",
- "xray_install_hint": "Ставит VLESS + XHTTP + TLS (Xray-core). Рекомендуется токен Cloudflare (DNS-01) — TCP 80 не занимается.",
- "xray_ports_warning": "На время установки TCP 80 должен быть свободен (Let\u0027s Encrypt HTTP-01). Переустановка заменяет предыдущий Xray.",
- "xray_ports_warning_cf": "Cloudflare DNS ACME не использует порт 80. Токену нужно Zone → DNS → Edit. Прокси выключите (серое облако). Переустановка заменяет предыдущий Xray.",
+ "xray_install_hint": "Ставит VLESS + XHTTP + TLS (Xray-core). Рекомендуется токен Cloudflare (DNS-01) — TCP 80 не занимается. Порт 443 необязателен.",
+ "xray_ports_warning": "На время установки TCP 80 должен быть свободен (Let\u0027s Encrypt HTTP-01). Слушающий порт — 443 или любой свободный. Переустановка заменяет предыдущий Xray.",
+ "xray_ports_warning_cf": "Cloudflare DNS ACME не использует порт 80. Слушайте на 443 или любом свободном TCP. Токену нужно Zone → DNS → Edit. Прокси выключите (серое облако).",
+ "xray_listen_port": "Порт прослушивания (TCP)",
+ "xray_port_standard": "Стандартный — 443/TCP",
+ "xray_port_custom": "Свой порт",
+ "xray_port_hint": "Любой свободный TCP-порт (по умолчанию 8443). Откройте его в файрволе. Порт 443 не занимает.",
+ "xray_port_hint_standard": "Использует 443/TCP. Убедитесь, что 443 на сервере свободен.",
"xray_acme_method": "Способ выпуска SSL",
"xray_acme_cloudflare": "Cloudflare DNS (API-токен) — рекомендуется, без порта 80",
"xray_acme_http": "HTTP-01 — нужен свободный TCP 80",
diff --git a/translations/zh.json b/translations/zh.json
index d43bf8f..fe396f2 100644
--- a/translations/zh.json
+++ b/translations/zh.json
@@ -80,15 +80,20 @@
"no_connections_desc": "添加首个连接以生成 VPN 配置文件",
"install_protocol": "安装协议",
"port_default_hint": "默认端口: 55424。请确保端口未被占用。",
- "port_xray_hint": "Default 443/TCP. Prefer Cloudflare DNS ACME — port 80 is not needed.",
- "port_xray_hint_cf": "Default 443/TCP. Certificate via Cloudflare DNS — port 80 is not used.",
+ "port_xray_hint": "Choose standard 443/TCP or any free custom TCP port. Prefer Cloudflare DNS ACME — port 80 is not needed.",
+ "port_xray_hint_cf": "Choose 443 or a custom TCP port. Certificate via Cloudflare DNS — port 80 is not used.",
"xray_domain": "Domain",
"xray_email": "Let\u0027s Encrypt email",
"xray_dns_hint": "Create DNS record:",
"xray_dns_hint_cf": "Domain must be on Cloudflare. A-record (DNS only):",
- "xray_install_hint": "Installs VLESS + XHTTP + TLS. Prefer Cloudflare API token (DNS-01) so TCP 80 stays free.",
- "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01).",
- "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_install_hint": "Installs VLESS + XHTTP + TLS. Prefer Cloudflare API token (DNS-01) so TCP 80 stays free. Port 443 is optional.",
+ "xray_ports_warning": "TCP 80 must be free during install (Let\u0027s Encrypt HTTP-01). Listen port can be 443 or any free TCP port.",
+ "xray_ports_warning_cf": "Cloudflare DNS ACME does not use port 80. Listen on 443 or any free TCP port. Token needs Zone → DNS → Edit. Keep proxy off (grey cloud).",
+ "xray_listen_port": "Listen port (TCP)",
+ "xray_port_standard": "Standard — 443/TCP",
+ "xray_port_custom": "Custom port",
+ "xray_port_hint": "Any free TCP port (default 8443). Open it in the firewall. Does not occupy 443.",
+ "xray_port_hint_standard": "Uses 443/TCP. Make sure nothing else is bound to 443 on this server.",
"xray_acme_method": "Certificate method",
"xray_acme_cloudflare": "Cloudflare DNS (API token) — recommended, no port 80",
"xray_acme_http": "HTTP-01 — needs free TCP 80",