From 9a07b7b74cbcc93037e4f1bfa3cf8ae4d546c108 Mon Sep 17 00:00:00 2001
From: Night Owl <176560581+ShadowOwlCode@users.noreply.github.com>
Date: Wed, 19 Mar 2025 19:27:35 +0330
Subject: [PATCH 1/6] feat: allow updating IPv4, IPv6, or domain in IP Address
Manager
fix: remove brackets from IPv6 URI when using a domain
---
core/scripts/hysteria2/show_user_uri.sh | 8 ++++--
menu.sh | 35 +++++++++++++++----------
2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/core/scripts/hysteria2/show_user_uri.sh b/core/scripts/hysteria2/show_user_uri.sh
index c86dc50..3ba648d 100644
--- a/core/scripts/hysteria2/show_user_uri.sh
+++ b/core/scripts/hysteria2/show_user_uri.sh
@@ -16,7 +16,7 @@ get_singbox_domain_and_port() {
get_normalsub_domain_and_port() {
if [ -f "$NORMALSUB_ENV" ]; then
- local domain port
+ local domain port subpath
domain=$(grep -E '^HYSTERIA_DOMAIN=' "$NORMALSUB_ENV" | cut -d'=' -f2)
port=$(grep -E '^HYSTERIA_PORT=' "$NORMALSUB_ENV" | cut -d'=' -f2)
subpath=$(grep -E '^SUBPATH=' "$NORMALSUB_ENV" | cut -d'=' -f2)
@@ -81,7 +81,11 @@ show_uri() {
local uri_base="hy2://$username%3A$authpassword@$ip:$port"
if [ "$ip_version" -eq 6 ]; then
- uri_base="hy2://$username%3A$authpassword@[$ip]:$port"
+ if [[ "$ip" =~ ^[0-9a-fA-F:]+$ ]]; then
+ uri_base="hy2://$username%3A$authpassword@[$ip]:$port"
+ else
+ uri_base="hy2://$username%3A$authpassword@$ip:$port"
+ fi
fi
local params=""
diff --git a/menu.sh b/menu.sh
index 17e8680..b39960c 100644
--- a/menu.sh
+++ b/menu.sh
@@ -323,35 +323,42 @@ hysteria2_change_sni_handler() {
edit_ips() {
while true; do
echo "======================================"
- echo " IP Address Manager "
+ echo " IP/Domain Address Manager "
echo "======================================"
- echo "1. Change IP4"
- echo "2. Change IP6"
+ echo "1. Change IPv4 or Domain"
+ echo "2. Change IPv6 or Domain"
echo "0. Back"
echo "======================================"
- read -p "Enter your choice [1-3]: " choice
+ read -p "Enter your choice [0-2]: " choice
case $choice in
1)
- read -p "Enter the new IPv4 address: " new_ip4
- if [[ $new_ip4 =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
- if [[ $(echo "$new_ip4" | awk -F. '{for (i=1;i<=NF;i++) if ($i>255) exit 1}') ]]; then
+ read -p "Enter the new IPv4 address or domain: " new_ip4_or_domain
+ if [[ $new_ip4_or_domain =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
+ if [[ $(echo "$new_ip4_or_domain" | awk -F. '{for (i=1;i<=NF;i++) if ($i>255) exit 1}') ]]; then
echo "Error: Invalid IPv4 address. Values must be between 0 and 255."
else
- python3 "$CLI_PATH" ip-address --edit -4 "$new_ip4"
+ python3 "$CLI_PATH" ip-address --edit -4 "$new_ip4_or_domain"
+ echo "IPv4 address has been updated to $new_ip4_or_domain."
fi
+ elif [[ $new_ip4_or_domain =~ ^[a-zA-Z0-9.-]+$ ]] && [[ ! $new_ip4_or_domain =~ [/:] ]]; then
+ python3 "$CLI_PATH" ip-address --edit -4 "$new_ip4_or_domain"
+ echo "Domain has been updated to $new_ip4_or_domain."
else
- echo "Error: Invalid IPv4 address format."
+ echo "Error: Invalid IPv4 or domain format."
fi
break
;;
2)
- read -p "Enter the new IPv6 address: " new_ip6
- if [[ $new_ip6 =~ ^(([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$ ]]; then
- python3 "$CLI_PATH" ip-address --edit -6 "$new_ip6"
- echo "IPv6 address has been updated to $new_ip6."
+ read -p "Enter the new IPv6 address or domain: " new_ip6_or_domain
+ if [[ $new_ip6_or_domain =~ ^(([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$ ]]; then
+ python3 "$CLI_PATH" ip-address --edit -6 "$new_ip6_or_domain"
+ echo "IPv6 address has been updated to $new_ip6_or_domain."
+ elif [[ $new_ip6_or_domain =~ ^[a-zA-Z0-9.-]+$ ]] && [[ ! $new_ip6_or_domain =~ [/:] ]]; then
+ python3 "$CLI_PATH" ip-address --edit -6 "$new_ip6_or_domain"
+ echo "Domain has been updated to $new_ip6_or_domain."
else
- echo "Error: Invalid IPv6 address format."
+ echo "Error: Invalid IPv6 or domain format."
fi
break
;;
From 552b0ab7ca12a903d6502e8a3d561315d6ef03f5 Mon Sep 17 00:00:00 2001
From: Night Owl <176560581+ShadowOwlCode@users.noreply.github.com>
Date: Wed, 19 Mar 2025 21:41:15 +0330
Subject: [PATCH 2/6] Fixed QR Code Display for Domain-Based URIs
---
core/scripts/webpanel/templates/users.html | 58 +++++++++++++---------
1 file changed, 35 insertions(+), 23 deletions(-)
diff --git a/core/scripts/webpanel/templates/users.html b/core/scripts/webpanel/templates/users.html
index 619af73..096e618 100644
--- a/core/scripts/webpanel/templates/users.html
+++ b/core/scripts/webpanel/templates/users.html
@@ -678,32 +678,48 @@
// QR Code Modal
$("#qrcodeModal").on("show.bs.modal", function (event) {
- const button = $(event.relatedTarget);
- const username = button.data("username");
- const configContainer = $(`#userConfigs-${username}`);
- const qrcodesContainer = $("#qrcodesContainer");
- qrcodesContainer.empty();
+ const button = $(event.relatedTarget);
+ const configContainer = $(`#userConfigs-${button.data("username")}`);
+ const qrcodesContainer = $("#qrcodesContainer");
+ qrcodesContainer.empty();
- configContainer.find(".config-container").each(function () {
- const configLink = $(this).data("link");
- const configType = $(this).find(".config-type").text().replace(":", "");
-
- // Create a card for each QR code
+ configContainer.find(".config-container").each(function () {
+ const configLink = $(this).data("link");
+ const configType = $(this).find(".config-type").text().replace(":", "");
+
+ let displayType = configType;
+
+ const hashMatch = configLink.match(/#(.+)$/);
+ if (hashMatch && hashMatch[1]) {
+ const hashValue = hashMatch[1];
+ if (hashValue.includes("IPv4") || hashValue.includes("IPv6")) {
+ displayType = hashValue;
+ }
+ } else if (configLink.includes("ipv4") || configLink.includes("IPv4")) {
+ displayType = "IPv4";
+ } else if (configLink.includes("ipv6") || configLink.includes("IPv6")) {
+ displayType = "IPv6";
+ }
+
+ const qrCodeId = `qrcode-${displayType}-${Math.random().toString(36).substring(2, 10)}`;
+
const card = $(`
-
+
-
-
${configType}
+
+
+
${displayType}
-
+
`);
qrcodesContainer.append(card);
const qrCodeStyling = new QRCodeStyling({
- width: 150,
- height: 150,
+ width: 180,
+ height: 180,
data: configLink,
+ margin: 5,
dotsOptions: {
color: "#212121",
type: "square"
@@ -720,15 +736,14 @@
}
});
- qrCodeStyling.append(document.getElementById(`qrcode-${configType}`));
-
- // Add click to copy functionality to the card
+ qrCodeStyling.append(document.getElementById(qrCodeId));
+
card.on("click", function () {
navigator.clipboard.writeText(configLink)
.then(() => {
Swal.fire({
icon: "success",
- title: configType + " link copied!",
+ title: displayType + " link copied!",
showConfirmButton: false,
timer: 1500,
});
@@ -745,12 +760,10 @@
});
});
- // Prevent modal from closing when clicking inside
$("#qrcodeModal .modal-content").on("click", function (e) {
e.stopPropagation();
});
- // Clear the QR code when the modal is hidden
$("#qrcodeModal").on("hidden.bs.modal", function () {
$("#qrcodesContainer").empty();
});
@@ -759,7 +772,6 @@
$("#qrcodeModal").modal("hide");
});
- // Search Functionality
function filterUsers() {
const searchText = $("#searchInput").val().toLowerCase();
From 5f28aa9d90eb7354c53c18f0b1f9ed45a92cb617 Mon Sep 17 00:00:00 2001
From: Night Owl <176560581+ShadowOwlCode@users.noreply.github.com>
Date: Wed, 19 Mar 2025 23:01:19 +0330
Subject: [PATCH 3/6] Added domain validation Modify Pydantic Models
---
.../routers/api/v1/schema/config/ip.py | 25 +++++++---
core/scripts/webpanel/templates/settings.html | 48 ++++++++-----------
2 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/core/scripts/webpanel/routers/api/v1/schema/config/ip.py b/core/scripts/webpanel/routers/api/v1/schema/config/ip.py
index 81a5667..adfa1ad 100644
--- a/core/scripts/webpanel/routers/api/v1/schema/config/ip.py
+++ b/core/scripts/webpanel/routers/api/v1/schema/config/ip.py
@@ -1,11 +1,24 @@
-from pydantic import BaseModel
-from ipaddress import IPv4Address, IPv6Address
-
+from pydantic import BaseModel, field_validator, ValidationInfo
+from ipaddress import IPv4Address, IPv6Address, ip_address
+import socket
class StatusResponse(BaseModel):
- ipv4: IPv4Address | None = None
- ipv6: IPv6Address | None = None
+ ipv4: str | None = None
+ ipv6: str | None = None
+ @field_validator('ipv4', 'ipv6', mode='before')
+ def check_ip_or_domain(cls, v: str, info: ValidationInfo):
+ if v is None:
+ return v
+ try:
+ ip_address(v)
+ return v
+ except ValueError:
+ try:
+ socket.getaddrinfo(v, None)
+ return v
+ except socket.gaierror:
+ raise ValueError(f"'{v}' is not a valid IPv4 or IPv6 address or domain name")
class EditInputBody(StatusResponse):
- pass
+ pass
\ No newline at end of file
diff --git a/core/scripts/webpanel/templates/settings.html b/core/scripts/webpanel/templates/settings.html
index 2ade890..3d30a40 100644
--- a/core/scripts/webpanel/templates/settings.html
+++ b/core/scripts/webpanel/templates/settings.html
@@ -264,14 +264,18 @@
if (!port) return false;
return /^[0-9]+$/.test(port) && parseInt(port) > 0 && parseInt(port) <= 65535;
}
- function isValidIP(ip, version) {
- if (!ip) return true;
+ function isValidIPorDomain(input) {
+ if (!input) return true;
+
+ const ipV4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
+ const ipV6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}([0-9a-fA-F]{1,4}|:)|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
+ const domainRegex = /^(?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}$/;
+ const lowerInput = input.toLowerCase();
+
+ if (ipV4Regex.test(input)) return true;
+ if (ipV6Regex.test(input)) return true;
+ if (domainRegex.test(lowerInput) && !lowerInput.startsWith("http://") && !lowerInput.startsWith("https://")) return true;
- if (version === 4) {
- return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip);
- } else if (version === 6) {
- return /^(([0-9a-fA-F]{1,4}:){7,7}([0-9a-fA-F]{1,4}|:)|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/.test(ip);
- }
return false;
}
@@ -352,15 +356,8 @@
} else {
input.removeClass('is-invalid');
}
- } else if (id === 'ipv4') {
- if (!isValidIP(input.val(), 4)) {
- input.addClass('is-invalid');
- isValid = false;
- } else {
- input.removeClass('is-invalid');
- }
- } else if (id === 'ipv6') {
- if (!isValidIP(input.val(), 6)) {
+ } else if (id === 'ipv4' || id === 'ipv6') { // Apply isValidIPorDomain for both IPv4 and IPv6
+ if (!isValidIPorDomain(input.val())) {
input.addClass('is-invalid');
isValid = false;
} else {
@@ -447,15 +444,15 @@
$("#ipv4").val(data.ipv4 || "");
$("#ipv6").val(data.ipv6 || "");
- $("#ipv4").attr("placeholder", "Enter IPv4");
- $("#ipv6").attr("placeholder", "Enter IPv6");
+ $("#ipv4").attr("placeholder", "Enter IPv4 or Domain");
+ $("#ipv6").attr("placeholder", "Enter IPv6 or Domain");
},
error: function () {
console.error("Failed to fetch IP addresses.");
- $("#ipv4").attr("placeholder", "Enter IPv4");
- $("#ipv6").attr("placeholder", "Enter IPv6");
+ $("#ipv4").attr("placeholder", "Enter IPv4 or Domain");
+ $("#ipv6").attr("placeholder", "Enter IPv6 or Domain");
}
});
@@ -695,21 +692,14 @@
$(this).addClass('is-invalid');
}
});
- $('#ipv4').on('input', function () {
- if (isValidIP($(this).val(), 4)) {
+ $('#ipv4, #ipv6').on('input', function () { // Apply to both ipv4 and ipv6
+ if (isValidIPorDomain($(this).val())) {
$(this).removeClass('is-invalid');
} else {
$(this).addClass('is-invalid');
}
});
- $('#ipv6').on('input', function () {
- if (isValidIP($(this).val(), 6)) {
- $(this).removeClass('is-invalid');
- } else {
- $(this).addClass('is-invalid');
- }
- });
$('#telegram_api_token, #telegram_admin_id').on('input', function () {
if ($(this).val().trim() !== "") {
$(this).removeClass('is-invalid');
From 207529b7e331f5ad1efa1aa6392432e6c43cfe54 Mon Sep 17 00:00:00 2001
From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com>
Date: Fri, 21 Mar 2025 20:48:50 +0330
Subject: [PATCH 4/6] Clone beta
---
install.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/install.sh b/install.sh
index 5bdefb8..bd2b6d0 100644
--- a/install.sh
+++ b/install.sh
@@ -53,7 +53,7 @@ else
echo "All required packages are already installed."
fi
-git clone https://github.com/ReturnFI/Hysteria2 /etc/hysteria
+git clone -b beta https://github.com/ReturnFI/Hysteria2 /etc/hysteria
cd /etc/hysteria
python3 -m venv hysteria2_venv
From b50b5ed3aab74d9a272c55f665c8a75e3022be3b Mon Sep 17 00:00:00 2001
From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com>
Date: Fri, 21 Mar 2025 21:15:34 +0330
Subject: [PATCH 5/6] Clone Main
---
install.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/install.sh b/install.sh
index bd2b6d0..5bdefb8 100644
--- a/install.sh
+++ b/install.sh
@@ -53,7 +53,7 @@ else
echo "All required packages are already installed."
fi
-git clone -b beta https://github.com/ReturnFI/Hysteria2 /etc/hysteria
+git clone https://github.com/ReturnFI/Hysteria2 /etc/hysteria
cd /etc/hysteria
python3 -m venv hysteria2_venv
From 9b3db7ce4b06a70126b049224ba6205852ecc4a8 Mon Sep 17 00:00:00 2001
From: Whispering Wind <151555003+ReturnFI@users.noreply.github.com>
Date: Fri, 21 Mar 2025 21:17:16 +0330
Subject: [PATCH 6/6] Update changelog
---
changelog | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/changelog b/changelog
index a1270a5..2953165 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1 @@
-🚀 feat: Add version check and notification new releases
-🛠️ refactor: Improve utils package structure with cleaner imports
-🐛 fix: IPv6 validation pattern
+🚀 feat: allow updating IPv4, IPv6, or domain in IP Address Manager