feat(ui): add masquerade controls and status to hysteria settings
This commit is contained in:
@ -7,6 +7,9 @@ $(document).ready(function () {
|
|||||||
checkObfs: contentSection.dataset.checkObfsUrl,
|
checkObfs: contentSection.dataset.checkObfsUrl,
|
||||||
enableObfs: contentSection.dataset.enableObfsUrl,
|
enableObfs: contentSection.dataset.enableObfsUrl,
|
||||||
disableObfs: contentSection.dataset.disableObfsUrl,
|
disableObfs: contentSection.dataset.disableObfsUrl,
|
||||||
|
checkMasquerade: contentSection.dataset.checkMasqueradeUrl,
|
||||||
|
enableMasquerade: contentSection.dataset.enableMasqueradeUrl,
|
||||||
|
disableMasquerade: contentSection.dataset.disableMasqueradeUrl,
|
||||||
setPortTemplate: contentSection.dataset.setPortUrlTemplate,
|
setPortTemplate: contentSection.dataset.setPortUrlTemplate,
|
||||||
setSniTemplate: contentSection.dataset.setSniUrlTemplate,
|
setSniTemplate: contentSection.dataset.setSniUrlTemplate,
|
||||||
updateGeoTemplate: contentSection.dataset.updateGeoUrlTemplate
|
updateGeoTemplate: contentSection.dataset.updateGeoUrlTemplate
|
||||||
@ -23,10 +26,10 @@ $(document).ready(function () {
|
|||||||
return /^[0-9]+$/.test(port) && parseInt(port) > 0 && parseInt(port) <= 65535;
|
return /^[0-9]+$/.test(port) && parseInt(port) > 0 && parseInt(port) <= 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmAction(actionName, callback) {
|
function confirmAction(title, text, callback) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: `Are you sure?`,
|
title: title,
|
||||||
text: `Do you really want to ${actionName}?`,
|
text: text,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
@ -53,13 +56,12 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
Swal.fire("Success!", successMessage, "success").then(() => {
|
const message = typeof response.detail === 'string' ? response.detail : successMessage;
|
||||||
if (showReload) {
|
Swal.fire("Success!", message, "success").then(() => {
|
||||||
|
if (showReload && !postSuccessCallback) {
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else if (postSuccessCallback) {
|
||||||
if (postSuccessCallback) {
|
postSuccessCallback(response);
|
||||||
postSuccessCallback(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -83,7 +85,6 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Swal.fire("Error!", errorMessage, "error");
|
Swal.fire("Error!", errorMessage, "error");
|
||||||
console.error("AJAX Error:", status, error, xhr.responseText);
|
|
||||||
},
|
},
|
||||||
complete: function() {
|
complete: function() {
|
||||||
if (buttonSelector) {
|
if (buttonSelector) {
|
||||||
@ -118,95 +119,122 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initUI() {
|
function initUI() {
|
||||||
$.ajax({
|
$.get(API_URLS.getPort, data => $("#hysteria_port").val(data.port || ""));
|
||||||
url: API_URLS.getPort,
|
$.get(API_URLS.getSni, data => $("#sni_domain").val(data.sni || ""));
|
||||||
type: "GET",
|
|
||||||
success: function (data) {
|
|
||||||
$("#hysteria_port").val(data.port || "");
|
|
||||||
},
|
|
||||||
error: function (xhr, status, error) {
|
|
||||||
console.error("Failed to fetch port:", error, xhr.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: API_URLS.getSni,
|
|
||||||
type: "GET",
|
|
||||||
success: function (data) {
|
|
||||||
$("#sni_domain").val(data.sni || "");
|
|
||||||
},
|
|
||||||
error: function (xhr, status, error) {
|
|
||||||
console.error("Failed to fetch SNI domain:", error, xhr.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchObfsStatus() {
|
function fetchAllStatuses() {
|
||||||
$.ajax({
|
Promise.all([
|
||||||
url: API_URLS.checkObfs,
|
$.ajax({ url: API_URLS.checkObfs, type: "GET" }),
|
||||||
type: "GET",
|
$.ajax({ url: API_URLS.checkMasquerade, type: "GET" })
|
||||||
success: function (data) {
|
]).then(([obfsResponse, masqueradeResponse]) => {
|
||||||
updateObfsUI(data.obfs);
|
const obfsStatus = obfsResponse.obfs;
|
||||||
},
|
const masqueradeStatus = masqueradeResponse.status;
|
||||||
error: function (xhr, status, error) {
|
|
||||||
$("#obfs_status_message").html('<span class="text-danger">Failed to fetch OBFS status.</span>');
|
const isObfsActive = obfsStatus === "OBFS is active.";
|
||||||
console.error("Failed to fetch OBFS status:", error, xhr.responseText);
|
const isMasqueradeActive = masqueradeStatus === "Enabled";
|
||||||
$("#obfs_enable_btn").hide();
|
|
||||||
$("#obfs_disable_btn").hide();
|
updateObfsUI(obfsStatus, isMasqueradeActive);
|
||||||
}
|
updateMasqueradeUI(masqueradeStatus, isObfsActive);
|
||||||
|
|
||||||
|
}).catch(error => {
|
||||||
|
console.error("Failed to fetch statuses:", error);
|
||||||
|
$("#obfs_status_message").html('<span class="text-danger">Failed to fetch status.</span>');
|
||||||
|
$("#masquerade_status_message").html('<span class="text-danger">Failed to fetch status.</span>');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateObfsUI(statusMessage, isMasqueradeEnabled) {
|
||||||
|
const container = $("#obfs_status_container");
|
||||||
|
const msgElement = $("#obfs_status_message");
|
||||||
|
const enableBtn = $("#obfs_enable_btn");
|
||||||
|
const disableBtn = $("#obfs_disable_btn");
|
||||||
|
|
||||||
function updateObfsUI(statusMessage) {
|
container.removeClass("border-success alert-success border-warning alert-warning border-danger alert-danger border-info alert-info");
|
||||||
$("#obfs_status_message").text(statusMessage);
|
enableBtn.hide();
|
||||||
if (statusMessage === "OBFS is active.") {
|
disableBtn.hide();
|
||||||
$("#obfs_enable_btn").hide();
|
|
||||||
$("#obfs_disable_btn").show();
|
if (isMasqueradeEnabled) {
|
||||||
$("#obfs_status_container").removeClass("border-danger border-warning alert-danger alert-warning").addClass("border-success alert-success");
|
msgElement.text("Cannot be managed while Masquerade is active.");
|
||||||
|
container.addClass("border-info alert-info");
|
||||||
|
} else if (statusMessage === "OBFS is active.") {
|
||||||
|
msgElement.text(statusMessage);
|
||||||
|
disableBtn.show();
|
||||||
|
container.addClass("border-success alert-success");
|
||||||
} else if (statusMessage === "OBFS is not active.") {
|
} else if (statusMessage === "OBFS is not active.") {
|
||||||
$("#obfs_enable_btn").show();
|
msgElement.text(statusMessage);
|
||||||
$("#obfs_disable_btn").hide();
|
enableBtn.show();
|
||||||
$("#obfs_status_container").removeClass("border-success border-danger alert-success alert-danger").addClass("border-warning alert-warning");
|
container.addClass("border-warning alert-warning");
|
||||||
} else {
|
} else {
|
||||||
$("#obfs_enable_btn").hide();
|
msgElement.html(`<span class="text-danger">${statusMessage}</span>`);
|
||||||
$("#obfs_disable_btn").hide();
|
container.addClass("border-danger alert-danger");
|
||||||
$("#obfs_status_container").removeClass("border-success border-warning alert-success alert-warning").addClass("border-danger alert-danger");
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMasqueradeUI(statusMessage, isObfsEnabled) {
|
||||||
|
const container = $("#masquerade_status_container");
|
||||||
|
const msgElement = $("#masquerade_status_message");
|
||||||
|
const enableBtn = $("#masquerade_enable_btn");
|
||||||
|
const disableBtn = $("#masquerade_disable_btn");
|
||||||
|
|
||||||
|
container.removeClass("border-success alert-success border-warning alert-warning border-danger alert-danger border-info alert-info");
|
||||||
|
enableBtn.hide();
|
||||||
|
disableBtn.hide();
|
||||||
|
|
||||||
|
if (isObfsEnabled) {
|
||||||
|
msgElement.text("Cannot be managed while OBFS is active.");
|
||||||
|
container.addClass("border-info alert-info");
|
||||||
|
} else if (statusMessage === "Enabled") {
|
||||||
|
msgElement.text(statusMessage);
|
||||||
|
disableBtn.show();
|
||||||
|
container.addClass("border-success alert-success");
|
||||||
|
} else if (statusMessage === "Disabled") {
|
||||||
|
msgElement.text(statusMessage);
|
||||||
|
enableBtn.show();
|
||||||
|
container.addClass("border-warning alert-warning");
|
||||||
|
} else {
|
||||||
|
msgElement.html(`<span class="text-danger">${statusMessage}</span>`);
|
||||||
|
container.addClass("border-danger alert-danger");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableObfs() {
|
function enableObfs() {
|
||||||
confirmAction("enable OBFS", function () {
|
confirmAction(
|
||||||
sendRequest(
|
"Enable OBFS?",
|
||||||
API_URLS.enableObfs,
|
"This will require all users to update their configuration files to reconnect.",
|
||||||
"GET",
|
() => sendRequest(API_URLS.enableObfs, "GET", null, "OBFS enabled successfully!", "#obfs_enable_btn", false, fetchAllStatuses)
|
||||||
null,
|
);
|
||||||
"OBFS enabled successfully!",
|
|
||||||
"#obfs_enable_btn",
|
|
||||||
false,
|
|
||||||
fetchObfsStatus
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableObfs() {
|
function disableObfs() {
|
||||||
confirmAction("disable OBFS", function () {
|
confirmAction(
|
||||||
sendRequest(
|
"Disable OBFS?",
|
||||||
API_URLS.disableObfs,
|
"This will disconnect all current users. They must update their configurations to reconnect.",
|
||||||
"GET",
|
() => sendRequest(API_URLS.disableObfs, "GET", null, "OBFS disabled successfully!", "#obfs_disable_btn", false, fetchAllStatuses)
|
||||||
null,
|
);
|
||||||
"OBFS disabled successfully!",
|
}
|
||||||
"#obfs_disable_btn",
|
|
||||||
false,
|
function enableMasquerade() {
|
||||||
fetchObfsStatus
|
confirmAction(
|
||||||
);
|
"Enable Masquerade?",
|
||||||
});
|
"This will enable the string masquerade mode.",
|
||||||
|
() => sendRequest(API_URLS.enableMasquerade, "GET", null, "Masquerade enabled successfully!", "#masquerade_enable_btn", false, fetchAllStatuses)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableMasquerade() {
|
||||||
|
confirmAction(
|
||||||
|
"Disable Masquerade?",
|
||||||
|
"This will disable the masquerade feature.",
|
||||||
|
() => sendRequest(API_URLS.disableMasquerade, "GET", null, "Masquerade disabled successfully!", "#masquerade_disable_btn", false, fetchAllStatuses)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changePort() {
|
function changePort() {
|
||||||
if (!validateForm('port_form')) return;
|
if (!validateForm('port_form')) return;
|
||||||
const port = $("#hysteria_port").val();
|
const port = $("#hysteria_port").val();
|
||||||
const url = API_URLS.setPortTemplate.replace("PORT_PLACEHOLDER", port);
|
const url = API_URLS.setPortTemplate.replace("PORT_PLACEHOLDER", port);
|
||||||
confirmAction("change the port", function () {
|
confirmAction("Are you sure?", "Do you really want to change the port?", () => {
|
||||||
sendRequest(url, "GET", null, "Port changed successfully!", "#port_change");
|
sendRequest(url, "GET", null, "Port changed successfully!", "#port_change");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -215,7 +243,7 @@ $(document).ready(function () {
|
|||||||
if (!validateForm('sni_form')) return;
|
if (!validateForm('sni_form')) return;
|
||||||
const domain = $("#sni_domain").val();
|
const domain = $("#sni_domain").val();
|
||||||
const url = API_URLS.setSniTemplate.replace("SNI_PLACEHOLDER", domain);
|
const url = API_URLS.setSniTemplate.replace("SNI_PLACEHOLDER", domain);
|
||||||
confirmAction("change the SNI", function () {
|
confirmAction("Are you sure?", "Do you really want to change the SNI?", () => {
|
||||||
sendRequest(url, "GET", null, "SNI changed successfully!", "#sni_change");
|
sendRequest(url, "GET", null, "SNI changed successfully!", "#sni_change");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -225,42 +253,29 @@ $(document).ready(function () {
|
|||||||
const buttonId = `#geo_update_${country}`;
|
const buttonId = `#geo_update_${country}`;
|
||||||
const url = API_URLS.updateGeoTemplate.replace('COUNTRY_PLACEHOLDER', country);
|
const url = API_URLS.updateGeoTemplate.replace('COUNTRY_PLACEHOLDER', country);
|
||||||
|
|
||||||
confirmAction(`update the Geo files for ${countryName}`, function () {
|
confirmAction(
|
||||||
sendRequest(
|
"Update Geo Files?",
|
||||||
url,
|
`Do you really want to update the Geo files for ${countryName}?`,
|
||||||
"GET",
|
() => sendRequest(url, "GET", null, `Geo files for ${countryName} updated successfully!`, buttonId, false, null)
|
||||||
null,
|
);
|
||||||
`Geo files for ${countryName} updated successfully!`,
|
|
||||||
buttonId,
|
|
||||||
false,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initUI();
|
initUI();
|
||||||
fetchObfsStatus();
|
fetchAllStatuses();
|
||||||
|
|
||||||
$("#port_change").on("click", changePort);
|
$("#port_change").on("click", changePort);
|
||||||
$("#sni_change").on("click", changeSNI);
|
$("#sni_change").on("click", changeSNI);
|
||||||
$("#obfs_enable_btn").on("click", enableObfs);
|
$("#obfs_enable_btn").on("click", enableObfs);
|
||||||
$("#obfs_disable_btn").on("click", disableObfs);
|
$("#obfs_disable_btn").on("click", disableObfs);
|
||||||
$("#geo_update_iran").on("click", function() { updateGeo('iran'); });
|
$("#masquerade_enable_btn").on("click", enableMasquerade);
|
||||||
$("#geo_update_china").on("click", function() { updateGeo('china'); });
|
$("#masquerade_disable_btn").on("click", disableMasquerade);
|
||||||
$("#geo_update_russia").on("click", function() { updateGeo('russia'); });
|
$("#geo_update_iran").on("click", () => updateGeo('iran'));
|
||||||
|
$("#geo_update_china").on("click", () => updateGeo('china'));
|
||||||
|
$("#geo_update_russia").on("click", () => updateGeo('russia'));
|
||||||
|
|
||||||
$('#sni_domain').on('input', function () {
|
$('#sni_domain, #hysteria_port').on('input', function () {
|
||||||
if (isValidDomain($(this).val())) {
|
const validator = $(this).attr('id') === 'sni_domain' ? isValidDomain : isValidPort;
|
||||||
$(this).removeClass('is-invalid');
|
if (validator($(this).val())) {
|
||||||
} else if ($(this).val().trim() !== "") {
|
|
||||||
$(this).addClass('is-invalid');
|
|
||||||
} else {
|
|
||||||
$(this).removeClass('is-invalid');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#hysteria_port').on('input', function () {
|
|
||||||
if (isValidPort($(this).val())) {
|
|
||||||
$(this).removeClass('is-invalid');
|
$(this).removeClass('is-invalid');
|
||||||
} else if ($(this).val().trim() !== "") {
|
} else if ($(this).val().trim() !== "") {
|
||||||
$(this).addClass('is-invalid');
|
$(this).addClass('is-invalid');
|
||||||
|
|||||||
@ -19,6 +19,9 @@
|
|||||||
data-check-obfs-url="{{ url_for('check_obfs') }}"
|
data-check-obfs-url="{{ url_for('check_obfs') }}"
|
||||||
data-enable-obfs-url="{{ url_for('enable_obfs') }}"
|
data-enable-obfs-url="{{ url_for('enable_obfs') }}"
|
||||||
data-disable-obfs-url="{{ url_for('disable_obfs') }}"
|
data-disable-obfs-url="{{ url_for('disable_obfs') }}"
|
||||||
|
data-check-masquerade-url="{{ url_for('check_masquerade') }}"
|
||||||
|
data-enable-masquerade-url="{{ url_for('enable_masquerade') }}"
|
||||||
|
data-disable-masquerade-url="{{ url_for('disable_masquerade') }}"
|
||||||
data-set-port-url-template="{{ url_for('set_port_api', port='PORT_PLACEHOLDER') }}"
|
data-set-port-url-template="{{ url_for('set_port_api', port='PORT_PLACEHOLDER') }}"
|
||||||
data-set-sni-url-template="{{ url_for('set_sni_api', sni='SNI_PLACEHOLDER') }}"
|
data-set-sni-url-template="{{ url_for('set_sni_api', sni='SNI_PLACEHOLDER') }}"
|
||||||
data-update-geo-url-template="{{ url_for('update_geo', country='COUNTRY_PLACEHOLDER') }}"
|
data-update-geo-url-template="{{ url_for('update_geo', country='COUNTRY_PLACEHOLDER') }}"
|
||||||
@ -63,22 +66,57 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-4">
|
<div class="col-md-6 mb-4">
|
||||||
<div class="card card-outline card-secondary h-100">
|
<div class="card card-outline card-secondary h-100">
|
||||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-user-secret"></i> OBFS</h3></div>
|
<div class="card-header p-0">
|
||||||
<div class="card-body">
|
<ul class="nav nav-tabs" id="obfs-masquerade-tabs" role="tablist">
|
||||||
<div class="mb-3">
|
<li class="nav-item">
|
||||||
<h5>OBFS Status</h5>
|
<a class="nav-link active" id="obfs-tab" data-toggle="tab" href="#obfs-content" role="tab" aria-controls="obfs-content" aria-selected="true"><i class="fas fa-user-secret"></i> OBFS</a>
|
||||||
<div id="obfs_status_container" class="p-3 border rounded">
|
</li>
|
||||||
<span id="obfs_status_message">Loading OBFS status...</span>
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="masquerade-tab" data-toggle="tab" href="#masquerade-content" role="tab" aria-controls="masquerade-content" aria-selected="false"><i class="fas fa-theater-masks"></i> Masquerade</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="card-body tab-content" id="obfs-masquerade-tabs-content">
|
||||||
|
<div class="tab-pane fade show active" id="obfs-content" role="tabpanel" aria-labelledby="obfs-tab">
|
||||||
|
<div class="mb-3">
|
||||||
|
<h5>OBFS Status</h5>
|
||||||
|
<div id="obfs_status_container" class="p-3 border rounded">
|
||||||
|
<span id="obfs_status_message">Loading OBFS status...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<strong>Warning:</strong> <small>Changing this setting requires users to update their client configurations.</small>
|
||||||
|
</div>
|
||||||
|
<button id="obfs_enable_btn" type='button' class='btn btn-success' style="display: none;">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>
|
||||||
|
Enable OBFS
|
||||||
|
</button>
|
||||||
|
<button id="obfs_disable_btn" type='button' class='btn btn-danger' style="display: none;">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>
|
||||||
|
Disable OBFS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="masquerade-content" role="tabpanel" aria-labelledby="masquerade-tab">
|
||||||
|
<div class="mb-3">
|
||||||
|
<h5>Masquerade Status</h5>
|
||||||
|
<div id="masquerade_status_container" class="p-3 border rounded">
|
||||||
|
<span id="masquerade_status_message">Loading Masquerade status...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
<strong>Note:</strong> <small>Masquerade cannot be active at the same time as OBFS.</small>
|
||||||
|
</div>
|
||||||
|
<button id="masquerade_enable_btn" type='button' class='btn btn-success' style="display: none;">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>
|
||||||
|
Enable Masquerade
|
||||||
|
</button>
|
||||||
|
<button id="masquerade_disable_btn" type='button' class='btn btn-danger' style="display: none;">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>
|
||||||
|
Disable Masquerade
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button id="obfs_enable_btn" type='button' class='btn btn-success' style="display: none;">
|
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>
|
|
||||||
Enable OBFS
|
|
||||||
</button>
|
|
||||||
<button id="obfs_disable_btn" type='button' class='btn btn-danger' style="display: none;">
|
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="display: none;"></span>
|
|
||||||
Disable OBFS
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user