Переглянути джерело

auto update script executed

Toby Chui 1 рік тому
батько
коміт
94f09482bb
4 змінених файлів з 258 додано та 18 видалено
  1. 1 1
      api.go
  2. 53 0
      mod/statistic/analytic/analytic.go
  3. 72 0
      mod/statistic/analytic/utils.go
  4. 132 17
      web/components/stats.html

+ 1 - 1
api.go

@@ -107,7 +107,7 @@ func initAPIs() {
 	//Zoraxy Analytic
 	authRouter.HandleFunc("/api/analytic/list", AnalyticLoader.HandleSummaryList)
 	authRouter.HandleFunc("/api/analytic/load", AnalyticLoader.HandleLoadTargetDaySummary)
-	//authRouter.HandleFunc("/api/analytic/list",)
+	authRouter.HandleFunc("/api/analytic/loadRange", AnalyticLoader.HandleLoadTargetRangeSummary)
 
 	//Network utilities
 	authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)

+ 53 - 0
mod/statistic/analytic/analytic.go

@@ -73,3 +73,56 @@ func (d *DataLoader) HandleLoadTargetDaySummary(w http.ResponseWriter, r *http.R
 	js, _ := json.Marshal(targetDailySummary)
 	utils.SendJSONResponse(w, string(js))
 }
+
+func (d *DataLoader) HandleLoadTargetRangeSummary(w http.ResponseWriter, r *http.Request) {
+	//Get the start date from POST para
+	start, err := utils.GetPara(r, "start")
+	if err != nil {
+		utils.SendErrorResponse(w, "start date cannot be empty")
+		return
+	}
+	if strings.Contains(start, "-") {
+		//Must be underscore
+		start = strings.ReplaceAll(start, "-", "_")
+	}
+	//Get end date from POST para
+	end, err := utils.GetPara(r, "end")
+	if err != nil {
+		utils.SendErrorResponse(w, "emd date cannot be empty")
+		return
+	}
+	if strings.Contains(end, "-") {
+		//Must be underscore
+		end = strings.ReplaceAll(end, "-", "_")
+	}
+
+	//Generate all the dates in between the range
+	keys, err := generateDateRange(start, end)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//Load all the data from database
+	dailySummaries := []*statistic.DailySummaryExport{}
+	for _, key := range keys {
+		thisStat := statistic.DailySummaryExport{}
+		err = d.Database.Read("stats", key, &thisStat)
+		if err == nil {
+			dailySummaries = append(dailySummaries, &thisStat)
+		}
+	}
+
+	//Merge the summaries into one
+	mergedSummary := mergeDailySummaryExports(dailySummaries)
+
+	js, _ := json.Marshal(struct {
+		Summary *statistic.DailySummaryExport
+		Records []*statistic.DailySummaryExport
+	}{
+		Summary: mergedSummary,
+		Records: dailySummaries,
+	})
+
+	utils.SendJSONResponse(w, string(js))
+}

+ 72 - 0
mod/statistic/analytic/utils.go

@@ -0,0 +1,72 @@
+package analytic
+
+import (
+	"fmt"
+	"time"
+
+	"imuslab.com/zoraxy/mod/statistic"
+)
+
+// Generate all the record keys from a given start and end dates
+func generateDateRange(startDate, endDate string) ([]string, error) {
+	layout := "2006_01_02"
+	start, err := time.Parse(layout, startDate)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing start date: %v", err)
+	}
+
+	end, err := time.Parse(layout, endDate)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing end date: %v", err)
+	}
+
+	var dateRange []string
+	for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
+		dateRange = append(dateRange, d.Format(layout))
+	}
+
+	return dateRange, nil
+}
+
+func mergeDailySummaryExports(exports []*statistic.DailySummaryExport) *statistic.DailySummaryExport {
+	mergedExport := &statistic.DailySummaryExport{
+		ForwardTypes:    make(map[string]int),
+		RequestOrigin:   make(map[string]int),
+		RequestClientIp: make(map[string]int),
+		Referer:         make(map[string]int),
+		UserAgent:       make(map[string]int),
+		RequestURL:      make(map[string]int),
+	}
+
+	for _, export := range exports {
+		mergedExport.TotalRequest += export.TotalRequest
+		mergedExport.ErrorRequest += export.ErrorRequest
+		mergedExport.ValidRequest += export.ValidRequest
+
+		for key, value := range export.ForwardTypes {
+			mergedExport.ForwardTypes[key] += value
+		}
+
+		for key, value := range export.RequestOrigin {
+			mergedExport.RequestOrigin[key] += value
+		}
+
+		for key, value := range export.RequestClientIp {
+			mergedExport.RequestClientIp[key] += value
+		}
+
+		for key, value := range export.Referer {
+			mergedExport.Referer[key] += value
+		}
+
+		for key, value := range export.UserAgent {
+			mergedExport.UserAgent[key] += value
+		}
+
+		for key, value := range export.RequestURL {
+			mergedExport.RequestURL[key] += value
+		}
+	}
+
+	return mergedExport
+}

+ 132 - 17
web/components/stats.html

@@ -6,20 +6,24 @@
     <div class="ui basic segment">
         <h2>Statistical Analysis</h2>
         <p>Statistic of your server in every aspects</p>
-        <div class="ui small input">
-            <input type="text" id="statsRangeStart" placeholder="From date">
-        </div>
-         To 
-        <div class="ui small input">
-            <input type="text" id="statsRangeEnd" placeholder="End date">
+        <div style="margin-bottom: 0.4em;">
+            <div class="ui small input">
+                <input type="text" id="statsRangeStart" placeholder="From date">
+            </div>
+            <span style="padding-left: 0.6em; padding-right: 0.6em;"> <i class="ui right arrow icon"></i> </span>
+            <div class="ui small input">
+                <input type="text" id="statsRangeEnd" placeholder="End date">
+            </div>
         </div>
-        <button onclick="handleLoadStatisticButtonPress();" class="ui basic button"><i class="search icon"></i> Search</button><br>
+        <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>
+        <br>
         <small>Leave end range as empty for showing starting day only statistic</small>
     </div>
     <div class="ui divider"></div>
     <div id="statisticRenderNotEnoughData" class="ui segment" style="padding: 2em;" align="center">
-        <h4 class="ui icon header" style="color: #7c7c7c !important;">
-            <i class="medium icons">
+        <h4 class="ui icon header">
+            <i class="medium icons" style="color: #dbdbdb !important;">
                 <i class="chart pie icon"></i>
                 <i class="small corner remove icon" style="
                     font-size: 1.4em;
@@ -28,14 +32,53 @@
                 "></i>
             </i>
             
-            <div class="content" >
-                Unable To Generate Analytics Report
+            <div class="content" style="margin-top: 1em; color: #7c7c7c !important;">
+                No Data 
                 <div class="sub header" style="color: #adadad !important;">The selected period contains too little or no request data collected. <br>
                     Please select a larger range or make sure there are enough traffic routing through this site.</div>
             </div>
         </h4>
     </div>
     <div id="statisticRenderElement" class="ui basic segment">
+        <!-- View Counts Statistics -->
+        <div class="ui three small statistics">
+            <div class="statistic">
+              <div class="value totalViewCount">
+                
+              </div>
+              <div class="label">
+                Total Requests
+              </div>
+            </div>
+            <div class="statistic">
+                <div class="value">
+                    <i class="ui green check circle icon"></i> <span class="totalSuccCount"></span>
+                </div>
+              <div class="label">
+                 Success Requests
+              </div>
+            </div>
+            <div class="statistic">
+              <div class="value">
+                <i class="ui red times circle icon"></i> <span class="totalErrorCount"></span>
+              </div>
+              <div class="label">
+                Error Requests
+              </div>
+            </div>
+        </div>
+        <!-- Forward Type Data -->
+        <h3>Forward Traffic Types</h3>
+        <p>Traffic forwarding type classified by their protocols and proxy rules.</p>
+        <table class="ui celled unstackable table">
+            <thead>
+                <tr><th>Forward Type</th>
+                <th>Counts</th>
+                <th>Percentage</th>
+            </tr></thead>
+            <tbody class="forwardTypeCounts">
+            </tbody>
+        </table>
         <!-- Client Geolocation Analysis-->
         <h3>Visitors Countries</h3>
         <p>Distributions of visitors by country code. Access origin are estimated using open source GeoIP database and might not be accurate.</p>
@@ -60,8 +103,8 @@
                     <table class="ui unstackable striped celled table">
                         <thead>
                           <tr>
-                            <th>Request Origin</th>
-                            <th>No of Requests</th>
+                            <th class="no-sort">Request Origin</th>
+                            <th class="no-sort">No of Requests</th>
                         </tr></thead>
                         <tbody id="stats_requestCountlist">
                          
@@ -115,7 +158,7 @@
             </div>
         </div>
     </div>
-    <button class="ui icon right floated basic button" onclick="initStatisticSummery();"><i class="green refresh icon"></i> Refresh</button>
+    <!-- <button class="ui icon right floated basic button" onclick="initStatisticSummery();"><i class="green refresh icon"></i> Refresh</button> -->
     <br><br>
 </div>
 <script>
@@ -132,7 +175,7 @@
                 loadStatisticByDate(targetDate);
             }else{
                 //Two dates are given and they are not identical
-                
+                loadStatisticByRange(startdate, enddate);
                 console.log(startdate, enddate);
 
             }
@@ -155,6 +198,14 @@
                 $("#statisticRenderNotEnoughData").hide();
             }
 
+            //Render the text values
+            $("#statisticRenderElement").find(".totalViewCount").text(abbreviateNumber(data.TotalRequest));
+            $("#statisticRenderElement").find(".totalSuccCount").text(abbreviateNumber(data.ValidRequest));
+            $("#statisticRenderElement").find(".totalErrorCount").text(abbreviateNumber(data.ErrorRequest));
+
+            //Render forward type data
+            renderForwardTypeCounts(data.ForwardTypes);
+            
             //Render visitor data
             renderVisitorChart(data.RequestOrigin);
 
@@ -166,10 +217,69 @@
         });
    }
    initStatisticSummery();
+   $("#statsRangeStart").val(getTodayStatisticKey().split("_").join("-"));
+
+   function loadStatisticByRange(startdate, endDate){
+        $.getJSON("/api/analytic/loadRange?start=" + startdate + "&end=" + endDate, function(data){
+            console.log(data);
+            //Destroy all the previous charts
+           
+            statisticCharts.forEach(function(thisChart){
+                thisChart.destroy();
+            })
+ 
+            if (data.Summary.TotalRequest == 0){
+                //No data to analysis
+                $("#statisticRenderElement").hide()
+                $("#statisticRenderNotEnoughData").show();
+                return;
+            }else{
+                $("#statisticRenderElement").show();
+                $("#statisticRenderNotEnoughData").hide();
+            }
+
+            //Render the text values
+            $("#statisticRenderElement").find(".totalViewCount").text(abbreviateNumber(data.Summary.TotalRequest));
+            $("#statisticRenderElement").find(".totalSuccCount").text(abbreviateNumber(data.Summary.ValidRequest));
+            $("#statisticRenderElement").find(".totalErrorCount").text(abbreviateNumber(data.Summary.ErrorRequest));
+
+            //Render forward type data
+            renderForwardTypeCounts(data.Summary.ForwardTypes);
+
+            //Render visitor data
+            renderVisitorChart(data.Summary.RequestOrigin);
+
+            //Render IP versions
+            renderIPVersionChart(data.Summary.RequestClientIp);
+
+            //Render user agent analysis
+            renderUserAgentCharts(data.Summary.UserAgent);
+            
+        });
+   }
 
    picker.attach({ target: document.getElementById("statsRangeStart") });
    picker.attach({ target: document.getElementById("statsRangeEnd") });
-   
+
+   function renderForwardTypeCounts(forwardTypes){
+        let tablBody = $("#statisticRenderElement").find(".forwardTypeCounts");
+        tablBody.empty();
+        let totalForwardCounts = 0;
+        for (let [key, value] of Object.entries(forwardTypes)) {
+            totalForwardCounts += value;
+        }
+
+        for (let [key, value] of Object.entries(forwardTypes)) {
+            tablBody.append(`<tr>
+                    <td>${key}</td>
+                    <td>${abbreviateNumber(value)} (${value})</td>
+                    <td>${((value/totalForwardCounts)*100).toFixed(3)}%</td>
+                </tr>
+            `);
+        }
+   }
+
+
    function getTodayStatisticKey(){
         var today = new Date();
         var year = today.getFullYear();
@@ -184,7 +294,7 @@
         var sd = $("#statsRangeStart").val();
         var ed = $("#statsRangeEnd").val();
         //Swap them if sd is later than ed
-        if (sd > ed) {
+        if (sd != "" && ed != "" && sd > ed) {
             ed = [sd, sd = ed][0];
             $("#statsRangeStart").val(sd);
             $("#statsRangeEnd").val(ed);
@@ -193,6 +303,11 @@
         initStatisticSummery(sd, ed);
    }
 
+   function clearStatisticDateRange(){
+        $("#statsRangeStart").val("");
+        $("#statsRangeEnd").val("");
+   }
+
     function renderUserAgentCharts(userAgentsEntries){
         let userAgents = Object.keys(userAgentsEntries);
         let requestCounts = Object.values(userAgentsEntries);