mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-12 08:05:19 +02:00
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:
@@ -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("");
|
||||
|
@@ -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");
|
||||
|
@@ -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>
|
||||
|
@@ -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));
|
||||
|
@@ -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">
|
||||
|
@@ -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
|
||||
*/
|
||||
|
144
src/web/snippet/advanceStatsOprs.html
Normal file
144
src/web/snippet/advanceStatsOprs.html
Normal 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>
|
Reference in New Issue
Block a user