Updates v2.6.2

+ Added advance stats operation tab
+ Added statistic reset #13
+ Added statistic export to csv and json (please use json)
+ Make subdomain clickable (not vdir) #12
+ Added TCP Proxy
+ Updates SMTP setup UI to make it more straight forward to setup
This commit is contained in:
Toby Chui
2023-06-04 23:59:56 +08:00
parent 9535abe314
commit 2574d0504e
16 changed files with 516 additions and 101 deletions

View File

@@ -15,8 +15,8 @@
<input type="text" id="statsRangeEnd" placeholder="End date">
</div>
</div>
<button onclick="handleLoadStatisticButtonPress();" class="ui basic button"><i class="blue search icon"></i> Search</button>
<button onclick="clearStatisticDateRange();" class="ui yellow basic button"><i class="eraser icon"></i> Clear Range</button>
<button onclick="handleLoadStatisticButtonPress();" class="ui basic button"><i class="blue search icon"></i> Load</button>
<button onclick="clearStatisticDateRange();" class="ui basic button"><i class="eraser icon"></i> Clear Search</button>
<br>
<small>Leave end range as empty for showing starting day only statistic</small>
</div>
@@ -193,7 +193,9 @@
<canvas id="requestTrends"></canvas>
</div>
</div>
<button onclick="showSideWrapper('snippet/advanceStatsOprs.html?t=' + Date.now() + '#' + encodeURIComponent(JSON.stringify(getStatisticDateRange())));" class="ui basic right floated black button"><i class="external square alternate icon"></i> Advance Operations</button>
</div>
<!-- <button class="ui icon right floated basic button" onclick="initStatisticSummery();"><i class="green refresh icon"></i> Refresh</button> -->
<br><br>
</div>
@@ -360,6 +362,28 @@
initStatisticSummery(sd, ed);
}
function getStatisticDateRange(){
var sd = $("#statsRangeStart").val();
var ed = $("#statsRangeEnd").val();
if (ed == ""){
ed = sd;
}
if (sd == "" && ed == ""){
var sk = getTodayStatisticKey();
sd = sk;
ed = sk;
}
//Swap them if sd is later than ed
if (sd != "" && ed != "" && sd > ed) {
ed = [sd, sd = ed][0];
}
return [sd, ed];
}
function clearStatisticDateRange(){
$("#statsRangeStart").val("");

View File

@@ -316,7 +316,16 @@
data: {set: thisValue},
success: function(data){
if (data.error != undefined){
alert(data.error);
msgbox(data.error, false, 8000);
//Restore backend value to make sure the UI is always in sync
$.get("/api/proxy/useHttpsRedirect", function(data){
if (data == true){
$("#redirect").checkbox("set checked");
}else{
$("#redirect").checkbox("set unchecked");
}
});
}else{
//Updated
msgbox("Setting Updated");

View File

@@ -42,8 +42,9 @@
if (subd.RequireTLS){
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
}
$("#subdList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
<td data-label="" editable="false">${subd.RootOrMatchingDomain}</td>
<td data-label="" editable="false"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a></td>
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
<td data-label="" editable="true" datatype="skipver">${!subd.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`}</td>
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>

View File

@@ -4,7 +4,7 @@
<p>Proxy traffic flow on layer 3 via TCP/IP</p>
</div>
<button class="ui basic orange button" id="addProxyConfigButton"><i class="ui add icon"></i> Add Proxy Config</button>
<button class="ui basic circular right floated icon button" title="Refresh List"><i class="ui green refresh icon"></i></button>
<button class="ui basic circular right floated icon button" onclick="initProxyConfigList();" title="Refresh List"><i class="ui green refresh icon"></i></button>
<div class="ui divider"></div>
<div class="ui basic segment" id="addproxyConfig" style="display:none;">
<h3>TCP Proxy Config</h3>
@@ -230,11 +230,13 @@
} else {
proxyConfigs.forEach(function(config) {
var runningLogo = '<i class="red circle icon"></i>';
var runningLogo = 'Stopped';
var runningClass = "stopped";
var startButton = `<button onclick="startTcpProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="play icon"></i> Start Proxy</button>`;
if (config.Running){
runningLogo = '<i class="green circle icon"></i>';
runningLogo = 'Running';
startButton = `<button onclick="stopTcpProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="red stop icon"></i> Stop Proxy</button>`;
runningClass = "running"
}
var modeText = "Unknown";
@@ -248,8 +250,10 @@
var thisConfig = encodeURIComponent(JSON.stringify(config));
var row = $(`<tr class="tcproxConfig" uuid="${config.UUID}" config="${thisConfig}">`);
row.append($('<td>').html(runningLogo + config.Name));
var row = $(`<tr class="tcproxConfig ${runningClass}" uuid="${config.UUID}" config="${thisConfig}">`);
row.append($('<td>').html(`
${config.Name}
<div class="statusText">${runningLogo}</div>`));
row.append($('<td>').text(config.PortA));
row.append($('<td>').text(config.PortB));
row.append($('<td>').text(modeText));

View File

@@ -117,6 +117,7 @@
<p>Results: <div id="ipRangeOutput">N/A</div></p>
</div>
<!-- System Information -->
<div class="ui divider"></div>
<div id="zoraxyinfo">
<h3 class="ui header">

View File

@@ -459,6 +459,33 @@ body{
user-select: none;
}
/*
TCP Proxy
*/
.tcproxConfig td:first-child{
position: relative;
}
.tcproxConfig.running td:first-child{
border-left: 0.6em solid #21ba45 !important;
}
.tcproxConfig.stopped td:first-child{
border-left: 0.6em solid #414141 !important;
}
.tcproxConfig td:first-child .statusText{
position: absolute;
bottom: 0.3em;
left: 0.2em;
font-size: 2em;
color:rgb(224, 224, 224);
opacity: 0.7;
pointer-events: none;
user-select: none;
}
/*
Uptime Monitor
*/

View File

@@ -0,0 +1,144 @@
<!DOCTYPE html>
<html>
<head>
<!-- Notes: This should be open in its original path-->
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script>
</head>
<body>
<br>
<div class="ui container">
<div class="ui header">
<div class="content">
Advance Statistics Operations
<div class="sub header">Selected Range: <span id="daterange"></span></div>
</div>
</div>
<div class="ui divider"></div>
<h3>Export Data</h3>
<p>You can export the statistics collected by Zoraxy in the selected range for further analysis</p>
<button class="ui basic teal button" onclick="handleExportAsCSV();"><i class="download icon"></i> Export CSV</button>
<button class="ui basic pink button" onclick="handleExportAsJSON();"><i class="download icon"></i> Export JSON</button>
<div class="ui divider"></div>
<h3>Reset Statistics</h3>
<p>You can reset the statistics within the selected time range for debug purpose. Note that this operation is irreversible.</p>
<button class="ui basic red button" onclick="handleResetStats();"><i class="trash icon"></i> RESET STATISTICS</button>
<br><br>
<button class="ui basic button iframeOnly" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Cancel</button>
</div>
<script>
let startDate = "";
let endDate = "";
/*
Actions Handler
*/
function handleExportAsJSON(){
window.open(`/api/analytic/exportRange?start=${startDate}&end=${endDate}&format=json`, 'download');
}
function handleExportAsCSV(){
window.open(`/api/analytic/exportRange?start=${startDate}&end=${endDate}&format=csv`, 'download');
}
function handleResetStats(){
if (confirm("Confirm remove statistics from " + startDate + " to " + endDate +"?")){
$.ajax({
url: "/api/analytic/resetRange?start=" + startDate + "&end=" + endDate,
method: "DELETE",
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false, 5000);
}else{
parent.msgbox("Statistic Cleared");
parent.hideSideWrapper();
}
}
})
}
}
/*
Data Loading
*/
function loadDateRange(){
if (window.location.hash.length > 1){
try{
var dateRange = JSON.parse(decodeURIComponent(window.location.hash.substr(1)));
startDate = dateRange[0].trim();
endDate = dateRange[1].trim();
//Check if they are valid dates
if (!isValidDateFormat(startDate)){
alert("Start date is not a valid date: " + startDate);
return
}
if (!isValidDateFormat(endDate)){
alert("End date is not a valid date: " + endDate);
return
}
//Sort the two dates if they are placed in invalid orders
var [s, e] = sortDates(startDate, endDate);
startDate = s;
endDate = e;
$("#daterange").html(startDate + ` <i class="arrow right icon" style="margin-right: 0;"></i> ` + endDate);
}catch(ex){
alert("Invalid usage: Invalid date range given");
}
}
}
loadDateRange();
function isValidDateFormat(dateString) {
// Create a regular expression pattern for the yyyy-mm-dd format
const pattern = /^\d{4}-\d{2}-\d{2}$/;
// Check if the input string matches the pattern
if (!pattern.test(dateString)) {
return false; // Invalid format
}
// Parse the date components
const year = parseInt(dateString.substring(0, 4), 10);
const month = parseInt(dateString.substring(5, 7), 10);
const day = parseInt(dateString.substring(8, 10), 10);
// Check if the parsed components represent a valid date
const date = new Date(year, month - 1, day);
if (
date.getFullYear() !== year ||
date.getMonth() + 1 !== month ||
date.getDate() !== day
) {
return false; // Invalid date
}
return true; // Valid date in yyyy-mm-dd format
}
function sortDates(date1, date2) {
// Parse the date strings
const parsedDate1 = new Date(date1);
const parsedDate2 = new Date(date2);
// Compare the parsed dates
if (parsedDate1 > parsedDate2) {
// Swap the dates
const temp = date1;
date1 = date2;
date2 = temp;
}
// Return the swapped dates
return [date1, date2];
}
</script>
</body>
</html>