From d96e711a34ce621b5a37e0506e8b315f0a5bf10a Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Fri, 19 Jan 2024 16:03:43 +0800
Subject: [PATCH 01/14] change ms and lost rate

---
 web/js/serverstatus.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index ce3a692..43c1237 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -82,7 +82,6 @@ function uptime() {
 						"<div id=\"expand_hdd\">加载中</div>" +
 						"<div id=\"expand_tupd\">加载中</div>" +
 						"<div id=\"expand_ping\">加载中</div>" +
-						"<div id=\"expand_lost\">加载中</div>" +
 						"<div id=\"expand_custom\">加载中</div>" +
 					"</div></td></tr>"
 				);
@@ -255,13 +254,14 @@ function uptime() {
 
 				// tcp, udp, process, thread count
 				ExpandRow[0].children["expand_tupd"].innerHTML = "TCP/UDP/进/线: " + result.servers[i].tcp_count + " / " + result.servers[i].udp_count + " / " + result.servers[i].process_count+ " / " + result.servers[i].thread_count;
-				ExpandRow[0].children["expand_ping"].innerHTML = "联通/电信/移动: " + result.servers[i].time_10010 + "ms / " + result.servers[i].time_189 + "ms / " + result.servers[i].time_10086 + "ms"
 
                 // ping
                 var PING_10010 = result.servers[i].ping_10010.toFixed(0);
                 var PING_189 = result.servers[i].ping_189.toFixed(0);
                 var PING_10086 = result.servers[i].ping_10086.toFixed(0);
-				ExpandRow[0].children["expand_lost"].innerHTML = "丢包:联通/电信/移动: " + PING_10010 + "% / " + PING_189 + "% / " + PING_10086 + "%"
+
+				// ping ms + lost rate
+				ExpandRow[0].children["expand_ping"].innerHTML = "CU/CT/CM: " + result.servers[i].time_10010 + "ms ("+result.servers[i].ping_10010.toFixed(0)+"%) / " + result.servers[i].time_189 + "ms ("+result.servers[i].ping_189.toFixed(0)+"%) / " + result.servers[i].time_10086 + "ms ("+result.servers[i].ping_10086.toFixed(0)+"%)"
 
                 if (PING_10010 >= 20 || PING_189 >= 20 || PING_10086 >= 20)
                     TableRow.children["ping"].children[0].children[0].className = "progress-bar progress-bar-danger";

From 71cadcfebe05d97fb609b2b380ff82213f01faa0 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Sun, 21 Jan 2024 21:09:37 +0800
Subject: [PATCH 02/14] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=9B=91=E6=8E=A7?=
 =?UTF-8?q?=E9=87=8D=E8=A6=81=E6=9C=8D=E5=8A=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 clients/client-linux.py  | 118 +++++++++++++++++++++++++++++++++++++--
 clients/client-psutil.py | 118 +++++++++++++++++++++++++++++++++++++--
 server/config.json       |  40 +++++++++----
 server/src/main.cpp      |  34 +++++++++++
 server/src/main.h        |  11 +++-
 web/css/dark.css         |  66 +++++++++-------------
 web/css/light.css        |  66 +++++++++-------------
 web/index.html           |  73 ++++++++++++++++--------
 web/js/serverstatus.js   |  42 +++++++++++---
 9 files changed, 434 insertions(+), 134 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 00e59d5..0574453 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -20,6 +20,7 @@ PING_PACKET_HISTORY_LEN = 100
 INTERVAL = 1
 
 import socket
+import ssl
 import time
 import timeit
 import re
@@ -29,10 +30,10 @@ import json
 import errno
 import subprocess
 import threading
-try:
-    from queue import Queue     # python3
-except ImportError:
-    from Queue import Queue     # python2
+if sys.version_info.major == 3:
+    from queue import Queue
+elif sys.version_info.major == 2:
+    from Queue import Queue
 
 def get_uptime():
     with open('/proc/uptime', 'r') as f:
@@ -150,6 +151,7 @@ diskIO = {
     'read': 0,
     'write': 0
 }
+monitorServer = {}
 
 def _ping_thread(host, mark, port):
     lostPacket = 0
@@ -314,6 +316,97 @@ def get_realtime_data():
         ti.daemon = True
         ti.start()
 
+
+def _monitor_thread(name, host, interval, type):
+    lostPacket = 0
+    packet_queue = Queue(maxsize=PING_PACKET_HISTORY_LEN)
+    while True:
+        if name not in monitorServer.keys():
+            monitorServer[name] = {
+                "type": type,
+                "dns_time": 0,
+                "connect_time": 0,
+                "download_time": 0,
+                "online_rate": 1
+            }
+        if packet_queue.full():
+            if packet_queue.get() == 0:
+                lostPacket -= 1
+        try:
+            if type == "http":
+                address = host.replace("http://", "")
+                m = timeit.default_timer()
+                if PROBE_PROTOCOL_PREFER == 'ipv4':
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET)[0][4][0]
+                else:
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET6)[0][4][0]
+                monitorServer[name]["dns_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k = socket.create_connection((IP, 80), timeout=6)
+                monitorServer[name]["connect_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k.sendall("GET / HTTP/1.2\r\nHost:{}\r\nConnection:close\r\n\r\n".format(address).encode('utf-8'))
+                response = b""
+                while True:
+                    data = k.recv(4096)
+                    if not data:
+                        break
+                    response += data
+                http_code = response.decode('utf-8').split('\r\n')[0].split()[1]
+                monitorServer[name]["download_time"] = int((timeit.default_timer() - m) * 1000)
+                k.close()
+                if http_code not in ['200', '204', '301', '302', '401']:
+                    raise Exception("http code not in 200, 204, 301, 302, 401")
+            elif type == "https":
+                context = ssl._create_unverified_context()
+                address = host.replace("https://", "")
+                m = timeit.default_timer()
+                if PROBE_PROTOCOL_PREFER == 'ipv4':
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET)[0][4][0]
+                else:
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET6)[0][4][0]
+                monitorServer[name]["dns_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k = socket.create_connection((IP, 443), timeout=6)
+                monitorServer[name]["connect_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                kk = context.wrap_socket(k, server_hostname=address)
+                kk.sendall("GET / HTTP/1.2\r\nHost:{}\r\nConnection:close\r\n\r\n".format(address).encode('utf-8'))
+                response = b""
+                while True:
+                    data = kk.recv(4096)
+                    if not data:
+                        break
+                    response += data
+                http_code = response.decode('utf-8').split('\r\n')[0].split()[1]
+                monitorServer[name]["download_time"] = int((timeit.default_timer() - m) * 1000)
+                kk.close()
+                k.close()
+                if http_code not in ['200', '204', '301', '302', '401']:
+                    raise Exception("http code not in 200, 204, 301, 302, 401")
+            elif type == "tcp":
+                m = timeit.default_timer()
+                if PROBE_PROTOCOL_PREFER == 'ipv4':
+                    IP = socket.getaddrinfo(host.split(":")[0], None, socket.AF_INET)[0][4][0]
+                else:
+                    IP = socket.getaddrinfo(host.split(":")[0], None, socket.AF_INET6)[0][4][0]
+                monitorServer[name]["dns_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k = socket.create_connection((IP, int(host.split(":")[1])), timeout=6)
+                monitorServer[name]["connect_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k.send(b"GET / HTTP/1.2\r\n\r\n")
+                k.recv(1024)
+                monitorServer[name]["download_time"] = int((timeit.default_timer() - m) * 1000)
+                k.close()
+            packet_queue.put(1)
+        except Exception as e:
+            lostPacket += 1
+            packet_queue.put(0)
+        if packet_queue.qsize() > 5:
+            monitorServer[name]["online_rate"] = 1 - float(lostPacket) / packet_queue.qsize()
+        time.sleep(interval)
+
 def byte_str(object):
     '''
     bytes to str, str to bytes
@@ -360,6 +453,20 @@ if __name__ == '__main__':
             if data.find("You are connecting via") < 0:
                 data = byte_str(s.recv(1024))
                 print(data)
+                for i in data.split('\n'):
+                    if "monitor" in i and "type" in i and "{" in i and "}" in i:
+                        jdata = json.loads(i[i.find("{"):i.find("}")+1])
+                        t = threading.Thread(
+                            target=_monitor_thread,
+                            kwargs={
+                                'name': jdata.get("name"),
+                                'host': jdata.get("host"),
+                                'interval': jdata.get("interval"),
+                                'type': jdata.get("type")
+                            }
+                        )
+                        t.daemon = True
+                        t.start()
 
             timer = 0
             check_ip = 0
@@ -378,7 +485,6 @@ if __name__ == '__main__':
                 Load_1, Load_5, Load_15 = os.getloadavg()
                 MemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory()
                 HDDTotal, HDDUsed = get_hdd()
-
                 array = {}
                 if not timer:
                     array['online' + str(check_ip)] = get_network(check_ip)
@@ -412,7 +518,7 @@ if __name__ == '__main__':
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
                 array['io_read'] = diskIO.get("read")
                 array['io_write'] = diskIO.get("write")
-
+                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100}%</code>" for k, v in monitorServer.items())
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
             raise
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index ceac788..c2b3928 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -21,6 +21,7 @@ PING_PACKET_HISTORY_LEN = 100
 INTERVAL = 1
 
 import socket
+import ssl
 import time
 import timeit
 import os
@@ -29,10 +30,10 @@ import json
 import errno
 import psutil
 import threading
-try:
-    from queue import Queue     # python3
-except ImportError:
-    from Queue import Queue     # python2
+if sys.version_info.major == 3:
+    from queue import Queue
+elif sys.version_info.major == 2:
+    from Queue import Queue
 
 def get_uptime():
     return int(time.time() - psutil.boot_time())
@@ -148,6 +149,7 @@ diskIO = {
     'read': 0,
     'write': 0
 }
+monitorServer = {}
 
 def _ping_thread(host, mark, port):
     lostPacket = 0
@@ -303,6 +305,97 @@ def get_realtime_data():
         ti.daemon = True
         ti.start()
 
+def _monitor_thread(name, host, interval, type):
+    lostPacket = 0
+    packet_queue = Queue(maxsize=PING_PACKET_HISTORY_LEN)
+    while True:
+        if name not in monitorServer.keys():
+            monitorServer[name] = {
+                "type": type,
+                "dns_time": 0,
+                "connect_time": 0,
+                "download_time": 0,
+                "online_rate": 1
+            }
+        if packet_queue.full():
+            if packet_queue.get() == 0:
+                lostPacket -= 1
+        try:
+            if type == "http":
+                address = host.replace("http://", "")
+                m = timeit.default_timer()
+                if PROBE_PROTOCOL_PREFER == 'ipv4':
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET)[0][4][0]
+                else:
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET6)[0][4][0]
+                monitorServer[name]["dns_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k = socket.create_connection((IP, 80), timeout=6)
+                monitorServer[name]["connect_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k.sendall("GET / HTTP/1.2\r\nHost:{}\r\nConnection:close\r\n\r\n".format(address).encode('utf-8'))
+                response = b""
+                while True:
+                    data = k.recv(4096)
+                    if not data:
+                        break
+                    response += data
+                http_code = response.decode('utf-8').split('\r\n')[0].split()[1]
+                monitorServer[name]["download_time"] = int((timeit.default_timer() - m) * 1000)
+                k.close()
+                if http_code not in ['200', '204', '301', '302', '401']:
+                    raise Exception("http code not in 200, 204, 301, 302, 401")
+            elif type == "https":
+                context = ssl._create_unverified_context()
+                address = host.replace("https://", "")
+                m = timeit.default_timer()
+                if PROBE_PROTOCOL_PREFER == 'ipv4':
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET)[0][4][0]
+                else:
+                    IP = socket.getaddrinfo(address, None, socket.AF_INET6)[0][4][0]
+                monitorServer[name]["dns_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k = socket.create_connection((IP, 443), timeout=6)
+                monitorServer[name]["connect_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                kk = context.wrap_socket(k, server_hostname=address)
+                kk.sendall("GET / HTTP/1.2\r\nHost:{}\r\nConnection:close\r\n\r\n".format(address).encode('utf-8'))
+                response = b""
+                while True:
+                    data = kk.recv(4096)
+                    if not data:
+                        break
+                    response += data
+                http_code = response.decode('utf-8').split('\r\n')[0].split()[1]
+                monitorServer[name]["download_time"] = int((timeit.default_timer() - m) * 1000)
+                kk.close()
+                k.close()
+                if http_code not in ['200', '204', '301', '302', '401']:
+                    raise Exception("http code not in 200, 204, 301, 302, 401")
+            elif type == "tcp":
+                m = timeit.default_timer()
+                if PROBE_PROTOCOL_PREFER == 'ipv4':
+                    IP = socket.getaddrinfo(host.split(":")[0], None, socket.AF_INET)[0][4][0]
+                else:
+                    IP = socket.getaddrinfo(host.split(":")[0], None, socket.AF_INET6)[0][4][0]
+                monitorServer[name]["dns_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k = socket.create_connection((IP, int(host.split(":")[1])), timeout=6)
+                monitorServer[name]["connect_time"] = int((timeit.default_timer() - m) * 1000)
+                m = timeit.default_timer()
+                k.send(b"GET / HTTP/1.2\r\n\r\n")
+                k.recv(1024)
+                monitorServer[name]["download_time"] = int((timeit.default_timer() - m) * 1000)
+                k.close()
+            packet_queue.put(1)
+        except Exception as e:
+            lostPacket += 1
+            packet_queue.put(0)
+        if packet_queue.qsize() > 5:
+            monitorServer[name]["online_rate"] = 1 - float(lostPacket) / packet_queue.qsize()
+        time.sleep(interval)
+
+
 def byte_str(object):
     '''
     bytes to str, str to bytes
@@ -349,6 +442,20 @@ if __name__ == '__main__':
             if data.find("You are connecting via") < 0:
                 data = byte_str(s.recv(1024))
                 print(data)
+                for i in data.split('\n'):
+                    if "monitor" in i and "type" in i and "{" in i and "}" in i:
+                        jdata = json.loads(i[i.find("{"):i.find("}")+1])
+                        t = threading.Thread(
+                            target=_monitor_thread,
+                            kwargs={
+                                'name': jdata.get("name"),
+                                'host': jdata.get("host"),
+                                'interval': jdata.get("interval"),
+                                'type': jdata.get("type")
+                            }
+                        )
+                        t.daemon = True
+                        t.start()
 
             timer = 0
             check_ip = 0
@@ -368,7 +475,6 @@ if __name__ == '__main__':
                 MemoryTotal, MemoryUsed = get_memory()
                 SwapTotal, SwapUsed = get_swap()
                 HDDTotal, HDDUsed = get_hdd()
-
                 array = {}
                 if not timer:
                     array['online' + str(check_ip)] = get_network(check_ip)
@@ -402,7 +508,7 @@ if __name__ == '__main__':
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
                 array['io_read'] = diskIO.get("read")
                 array['io_write'] = diskIO.get("write")
-
+                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100}%</code>" for k, v in monitorServer.items())
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
             raise
diff --git a/server/config.json b/server/config.json
index c60018f..29a4df4 100644
--- a/server/config.json
+++ b/server/config.json
@@ -38,6 +38,26 @@
 			"monthstart": 1
 		}
 	],
+	"monitors": [
+		{
+			"name": "百度一下",
+			"host": "https://www.baidu.com",
+			"interval": 60,
+			"type": "https"
+		},
+		{
+			"name": "主机交流",
+			"host": "https://www.hostloc.com",
+			"interval": 60,
+			"type": "https"
+		},
+		{
+			"name": "DNS服务",
+			"host": "114.114.114.114:53",
+			"interval": 60,
+			"type": "tcp"
+		}
+	],
 	"watchdog": [
 		{
 			"name": "cpu high warning,exclude username s01",
@@ -58,17 +78,17 @@
 			"callback": "https://yourSMSurl"
 		},
 		{
-                        "name": "ddcc attack,limit type Oracle",
-                        "rule": "tcp_count>600&type='Oracle'",
-                        "interval": 300,
-                        "callback": "https://yourSMSurl"
-                },
+			"name": "ddcc attack,limit type Oracle",
+			"rule": "tcp_count>600&type='Oracle'",
+			"interval": 300,
+			"callback": "https://yourSMSurl"
+		},
 		{
-                        "name": "month traffic warning",
-                        "rule": "(network_out-last_network_out)/1024/1024/1024>999",
-                        "interval": 3600,
-                        "callback": "https://yourSMSurl"
-                },
+			"name": "month traffic warning",
+			"rule": "(network_out-last_network_out)/1024/1024/1024>999",
+			"interval": 3600,
+			"callback": "https://yourSMSurl"
+		},
 		{
 			"name": "you can parse an expression combining any known field",
 			"rule": "load_5>3",
diff --git a/server/src/main.cpp b/server/src/main.cpp
index d24d605..2dc5c43 100644
--- a/server/src/main.cpp
+++ b/server/src/main.cpp
@@ -92,6 +92,18 @@ void CMain::OnNewClient(int ClientNetID, int ClientID)
 		Client(ClientID)->m_Stats.m_Online4 = true;
 	else if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV6)
 		Client(ClientID)->m_Stats.m_Online6 = true;
+
+    // Send monitor to client
+    // support by cpp.la
+    int ID = 0;
+    char monitorBuffer[2048];
+    while (strcmp(Monitors(ID)->m_aName, "NULL"))
+    {
+        memset(monitorBuffer, 0, sizeof(monitorBuffer));
+        sprintf(monitorBuffer, "{\"name\":\"%s\",\"host\":\"%s\",\"interval\":%d,\"type\":\"%s\",\"monitor\":%d}", Monitors(ID)->m_aName, Monitors(ID)->m_aHost, Monitors(ID)->m_aInterval, Monitors(ID)->m_aType, ID);
+        m_Server.Network()->Send(ClientNetID, monitorBuffer);
+        ID++;
+    }
 }
 
 void CMain::OnDelClient(int ClientNetID)
@@ -572,6 +584,28 @@ int CMain::ReadConfig()
     } else
         str_copy(Watchdog(ID)->m_aName, "NULL", sizeof(Watchdog(ID)->m_aName));
 
+    // monitor
+    // support by: https://cpp.la
+    ID = 0;
+    const json_value &mStart = (*pJsonData)["monitors"];
+    if(mStart.type == json_array)
+    {
+        for(unsigned i = 0; i < mStart.u.array.length; i++)
+        {
+            if(ID < 0 || ID >= NET_MAX_CLIENTS)
+                continue;
+
+            str_copy(Monitors(ID)->m_aName, mStart[i]["name"].u.string.ptr, sizeof(Monitors(ID)->m_aName));
+            str_copy(Monitors(ID)->m_aHost, mStart[i]["host"].u.string.ptr, sizeof(Monitors(ID)->m_aHost));
+            Monitors(ID)->m_aInterval = mStart[i]["interval"].u.integer;
+            str_copy(Monitors(ID)->m_aType, mStart[i]["type"].u.string.ptr, sizeof(Monitors(ID)->m_aType));
+
+            ID++;
+        }
+        str_copy(Monitors(ID)->m_aName, "NULL", sizeof(Monitors(ID)->m_aName));
+    } else
+        str_copy(Monitors(ID)->m_aName, "NULL", sizeof(Monitors(ID)->m_aName));
+
 	// if file exists, read last network traffic record,reset m_LastNetworkIN and m_LastNetworkOUT
 	// support by: https://cpp.la
     IOHANDLE nFile = io_open(m_Config.m_aJSONFile, IOFLAG_READ);
diff --git a/server/src/main.h b/server/src/main.h
index eb3fb3e..848d246 100644
--- a/server/src/main.h
+++ b/server/src/main.h
@@ -77,7 +77,7 @@ class CMain
 			int64_t m_IORead;
 			int64_t m_IOWrite;
 			double m_CPU;
-			char m_aCustom[512];
+			char m_aCustom[1024];
 			// Options
 			bool m_Pong;
 		} m_Stats;
@@ -90,6 +90,13 @@ class CMain
 	    char m_aCallback[1024];
 	} m_aCWatchDogs[NET_MAX_CLIENTS];
 
+    struct CMonitors{
+        char m_aName[128];
+        char m_aHost[128];
+        int  m_aInterval;
+        char m_aType[128];
+    } m_aCMonitors[NET_MAX_CLIENTS];
+
 	struct CJSONUpdateThreadData
 	{
 		CClient *pClients;
@@ -108,6 +115,8 @@ public:
 	int Run();
 
     CWatchDog *Watchdog(int ruleID) { return &m_aCWatchDogs[ruleID]; }
+    CMonitors *Monitors(int ruleID) { return &m_aCMonitors[ruleID]; }
+
     void WatchdogMessage(int ClientNetID,
                          double load_1, double load_5, double load_15, double ping_10010, double ping_189, double ping_10086,
                          double time_10010, double time_189, double time_10086, double tcp_count, double udp_count, double process_count, double thread_count,
diff --git a/web/css/dark.css b/web/css/dark.css
index 5bfb949..eea98f3 100644
--- a/web/css/dark.css
+++ b/web/css/dark.css
@@ -23,48 +23,32 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 #ping { max-width: 110px; }
 
 @media only screen and (max-width: 1200px) {
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        #server {
+                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
+                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
+                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
+                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        }
 }
 @media only screen and (max-width: 720px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        #server {
+                body                                                            { font-size: 10px; }
+                .content                                                        { padding: 0; }
+                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
+                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
+                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
+                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        }
 }
 @media only screen and (max-width: 620px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-}
-@media only screen and (max-width: 533px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-}
-@media only screen and (max-width: 450px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-        #name, tr td:nth-child(3)                       { min-width: 55px; max-width: 85px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-        #cpu, #ram, #hdd                                        { min-width: 25px; max-width: 50px; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-}
+        #server {
+                body                                                            { font-size: 10px; }
+                .content                                                        { padding: 0; }
+                #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
+                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
+                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
+                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
+                #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
+                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        }
+}
\ No newline at end of file
diff --git a/web/css/light.css b/web/css/light.css
index e881dcb..37e7f77 100644
--- a/web/css/light.css
+++ b/web/css/light.css
@@ -20,48 +20,32 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 #ping { max-width: 110px; }
 
 @media only screen and (max-width: 1200px) {
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        #server {
+                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
+                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
+                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
+                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        }
 }
 @media only screen and (max-width: 720px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        #server {
+                body                                                            { font-size: 10px; }
+                .content                                                        { padding: 0; }
+                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
+                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
+                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
+                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        }
 }
 @media only screen and (max-width: 620px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-}
-@media only screen and (max-width: 533px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-}
-@media only screen and (max-width: 450px) {
-        body                                                            { font-size: 10px; }
-        .content                                                        { padding: 0; }
-        #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-        #name, tr td:nth-child(3)                       { min-width: 55px; max-width: 85px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
-        #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-        #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-        #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-        #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-        #cpu, #ram, #hdd                                        { min-width: 25px; max-width: 50px; }
-        #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-}
+        #server {
+                body                                                            { font-size: 10px; }
+                .content                                                        { padding: 0; }
+                #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
+                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
+                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
+                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
+                #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
+                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
+        }
+}
\ No newline at end of file
diff --git a/web/index.html b/web/index.html
index ed68d63..5fa6175 100644
--- a/web/index.html
+++ b/web/index.html
@@ -47,6 +47,12 @@
 					</div>
 					<div class="navbar-collapse collapse">
 						<ul class="nav navbar-nav">
+							<li class="dropdown">
+								<a data-toggle="tab" class="dropdown-toggle" href="#server">首页</a>
+							</li>
+							<li class="dropdown">
+								<a data-toggle="tab" class="dropdown-toggle" href="#monitor">服务</a>
+							</li>
 							<li class="dropdown">
 								<a data-toggle="dropdown" class="dropdown-toggle" href="#">风格<b class="caret"></b></a>
 								<ul class="dropdown-menu">
@@ -73,28 +79,51 @@
 				</div>
 				<p></p>
 			</div>
-			<table class="table table-striped table-condensed table-hover">
-				<thead>
-				<tr>
-					<th id="online_status" style="text-align: center;">🔗协议</th>
-					<th id="month_traffic" style="text-align: center;">📊月流量↓|↑</th>
-					<th id="name">📌节点</th>
-					<th id="type">🗂️虚拟化</th>
-					<th id="location">🌍位置</th>
-					<th id="uptime">⏱️在线</th>
-					<th id="load">负载</th>
-					<th id="network">🚦网络↓|↑</th>
-					<th id="traffic">📋总流量↓|↑</th>
-					<th id="cpu">🎯核芯</th>
-					<th id="ram">⚡️内存</th>
-					<th id="hdd">💾硬盘</th>
-					<th id="ping">🌐CU|CT|CM</th>
-				</tr>
-				</thead>
-				<tbody id="servers">
-				<!-- Servers here \o/ -->
-				</tbody>
-			</table>
+
+			<div class="tab-content">
+				<div class="tab-pane fade in active" id="server">
+					<!--主机-->
+					<table class="table table-striped table-condensed table-hover">
+						<thead>
+						<tr>
+							<th id="online_status" style="text-align: center;">🔗协议</th>
+							<th id="month_traffic" style="text-align: center;">📊月流量↓|↑</th>
+							<th id="name">📌节点</th>
+							<th id="type">🗂️虚拟化</th>
+							<th id="location">🌍位置</th>
+							<th id="uptime">⏱️在线</th>
+							<th id="load">负载</th>
+							<th id="network">🚦网络↓|↑</th>
+							<th id="traffic">📋总流量↓|↑</th>
+							<th id="cpu">🎯核芯</th>
+							<th id="ram">⚡️内存</th>
+							<th id="hdd">💾硬盘</th>
+							<th id="ping">🌐CU|CT|CM</th>
+						</tr>
+						</thead>
+						<tbody id="servers">
+						<!-- Servers here \o/ -->
+						</tbody>
+					</table>
+				</div>
+				<div class="tab-pane fade" id="monitor">
+					<!--服务-->
+					<table class="table table-striped table-condensed table-hover">
+						<thead>
+						<tr>
+							<th id="monitor_status" style="text-align: center;">🔗协议</th>
+							<th id="monitor_node">📌监测节点</th>
+							<th id="monitor_location">🌍监测位置</th>
+							<th id="monitor_text" style="text-align: center;">📋监测内容</th>
+						</tr>
+						</thead>
+						<tbody id="monitors">
+						<!-- Monitors here \o/ -->
+						</tbody>
+					</table>
+				</div>
+			</div>
+
 			<br />
 			<div id="updated">Updating...</div>
 		</div>
diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index 43c1237..529456c 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -57,6 +57,7 @@ function uptime() {
 
 		for (var i = 0, rlen=result.servers.length; i < rlen; i++) {
 			var TableRow = $("#servers tr#r" + i);
+			var MableRow = $("#monitors tr#r" + i);
 			var ExpandRow = $("#servers #rt" + i);
 			var hack; // fuck CSS for making me do this
 			if(i%2) hack="odd"; else hack="even";
@@ -82,16 +83,28 @@ function uptime() {
 						"<div id=\"expand_hdd\">加载中</div>" +
 						"<div id=\"expand_tupd\">加载中</div>" +
 						"<div id=\"expand_ping\">加载中</div>" +
-						"<div id=\"expand_custom\">加载中</div>" +
 					"</div></td></tr>"
 				);
 				TableRow = $("#servers tr#r" + i);
 				ExpandRow = $("#servers #rt" + i);
 				server_status[i] = true;
 			}
+			if (!MableRow.length) {
+				$("#monitors").append(
+					"<tr id=\"r" + i + "\" data-toggle=\"collapse\" data-target=\"#rt" + i + "\" class=\"accordion-toggle " + hack + "\">" +
+					"<td id=\"monitor_status\"><div class=\"progress\"><div style=\"width: 100%;\" class=\"progress-bar progress-bar-warning\"><small>加载中</small></div></div></td>" +
+					"<td id=\"monitor_node\">加载中</td>" +
+					"<td id=\"monitor_location\">加载中</td>" +
+					"<td id=\"monitor_text\">加载中</td>" +
+					"</tr>"
+				);
+				MableRow = $("#monitors tr#r" + i);
+			}
 			TableRow = TableRow[0];
+			MableRow = MableRow[0];
 			if(error) {
 				TableRow.setAttribute("data-target", "#rt" + i);
+				MableRow.setAttribute("data-target", "#rt" + i);
 				server_status[i] = true;
 			}
 
@@ -99,25 +112,35 @@ function uptime() {
 			if (result.servers[i].online4 && !result.servers[i].online6) {
 				TableRow.children["online_status"].children[0].children[0].className = "progress-bar progress-bar-success";
 				TableRow.children["online_status"].children[0].children[0].innerHTML = "<small>IPv4</small>";
+				MableRow.children["monitor_status"].children[0].children[0].className = "progress-bar progress-bar-success";
+				MableRow.children["monitor_status"].children[0].children[0].innerHTML = "<small>IPv4</small>";
 			} else if (result.servers[i].online4 && result.servers[i].online6) {
 				TableRow.children["online_status"].children[0].children[0].className = "progress-bar progress-bar-success";
 				TableRow.children["online_status"].children[0].children[0].innerHTML = "<small>双栈</small>";
+				MableRow.children["monitor_status"].children[0].children[0].className = "progress-bar progress-bar-success";
+				MableRow.children["monitor_status"].children[0].children[0].innerHTML = "<small>双栈</small>";
 			} else if (!result.servers[i].online4 && result.servers[i].online6) {
 			    TableRow.children["online_status"].children[0].children[0].className = "progress-bar progress-bar-success";
 				TableRow.children["online_status"].children[0].children[0].innerHTML = "<small>IPv6</small>";
+				MableRow.children["monitor_status"].children[0].children[0].className = "progress-bar progress-bar-success";
+				MableRow.children["monitor_status"].children[0].children[0].innerHTML = "<small>IPv6</small>";
 			} else {
 				TableRow.children["online_status"].children[0].children[0].className = "progress-bar progress-bar-danger";
 				TableRow.children["online_status"].children[0].children[0].innerHTML = "<small>关闭</small>";
+				MableRow.children["monitor_status"].children[0].children[0].className = "progress-bar progress-bar-danger";
+				MableRow.children["monitor_status"].children[0].children[0].innerHTML = "<small>关闭</small>";
 			}
 
 			// Name
 			TableRow.children["name"].innerHTML = result.servers[i].name;
+			MableRow.children["monitor_node"].innerHTML = result.servers[i].name;
 
 			// Type
 			TableRow.children["type"].innerHTML = result.servers[i].type;
 
 			// Location
 			TableRow.children["location"].innerHTML = result.servers[i].location;
+			MableRow.children["monitor_location"].innerHTML = result.servers[i].location;
 			if (!result.servers[i].online4 && !result.servers[i].online6) {
 				if (server_status[i]) {
 					TableRow.children["uptime"].innerHTML = "–";
@@ -138,15 +161,18 @@ function uptime() {
 					TableRow.children["ping"].children[0].children[0].className = "progress-bar progress-bar-danger";
 					TableRow.children["ping"].children[0].children[0].style.width = "100%";
 					TableRow.children["ping"].children[0].children[0].innerHTML = "<small>关闭</small>";
+					MableRow.children["monitor_text"].innerHTML = "-";
 					if(ExpandRow.hasClass("in")) {
 						ExpandRow.collapse("hide");
 					}
 					TableRow.setAttribute("data-target", "");
+					MableRow.setAttribute("data-target", "");
 					server_status[i] = false;
 				}
 			} else {
 				if (!server_status[i]) {
 					TableRow.setAttribute("data-target", "#rt" + i);
+					MableRow.setAttribute("data-target", "#rt" + i);
 					server_status[i] = true;
 				}
 
@@ -271,12 +297,8 @@ function uptime() {
                     TableRow.children["ping"].children[0].children[0].className = "progress-bar progress-bar-success";
 	            TableRow.children["ping"].children[0].children[0].innerHTML = PING_10010 + "%💻" + PING_189 + "%💻" + PING_10086 + "%";
 
-				// Custom
-				if (result.servers[i].custom) {
-					ExpandRow[0].children["expand_custom"].innerHTML = result.servers[i].custom
-				} else {
-					ExpandRow[0].children["expand_custom"].innerHTML = ""
-				}
+				// monitor
+				MableRow.children["monitor_text"].innerHTML = result.servers[i].custom;
 			}
 		};
 
@@ -286,9 +308,12 @@ function uptime() {
 		if (!error) {
 			$("#servers > tr.accordion-toggle").each(function(i) {
 				var TableRow = $("#servers tr#r" + i)[0];
+				var MableRow = $("#monitors tr#r" + i)[0];
 				var ExpandRow = $("#servers #rt" + i);
 				TableRow.children["online_status"].children[0].children[0].className = "progress-bar progress-bar-error";
 				TableRow.children["online_status"].children[0].children[0].innerHTML = "<small>错误</small>";
+				MableRow.children["monitor_status"].children[0].children[0].className = "progress-bar progress-bar-error";
+				MableRow.children["monitor_status"].children[0].children[0].innerHTML = "<small>错误</small>";
 				TableRow.children["month_traffic"].children[0].children[0].className = "progress-bar progress-bar-error";
 				TableRow.children["month_traffic"].children[0].children[0].innerHTML = "<small>错误</small>";
 				TableRow.children["uptime"].children[0].children[0].className = "progress-bar progress-bar-error";
@@ -311,10 +336,13 @@ function uptime() {
 				TableRow.children["ping"].children[0].children[0].className = "progress-bar progress-bar-error";
 				TableRow.children["ping"].children[0].children[0].style.width = "100%";
 				TableRow.children["ping"].children[0].children[0].innerHTML = "<small>错误</small>";
+				MableRow.children["monitor_text"].children[0].children[0].className = "progress-bar progress-bar-error";
+				MableRow.children["monitor_text"].children[0].children[0].innerHTML = "<small>错误</small>";
 				if(ExpandRow.hasClass("in")) {
 					ExpandRow.collapse("hide");
 				}
 				TableRow.setAttribute("data-target", "");
+				MableRow.setAttribute("data-target", "");
 				server_status[i] = false;
 			});
 		}

From 7e46b1062e49225a324726a3a9c3cac600430b8f Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Sun, 21 Jan 2024 21:34:07 +0800
Subject: [PATCH 03/14] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AF=B4=E6=98=8E?=
 =?UTF-8?q?=EF=BC=8C=E6=AF=94=E8=BE=83=E7=B2=97=E7=B3=99=E4=B8=8D=E5=BB=BA?=
 =?UTF-8?q?=E8=AE=AE=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 clients/client-linux.py  | 4 +++-
 clients/client-psutil.py | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 0574453..4d79bed 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -3,6 +3,7 @@
 # Update by : https://github.com/cppla/ServerStatus, Update date: 20220530
 # 版本:1.0.3, 支持Python版本:2.7 to 3.10
 # 支持操作系统: Linux, OSX, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures
+# ONLINE_PACKET_HISTORY_LEN, 探测间隔60s,记录24小时在线率(1440);探测间隔60s,记录7天(10080);探测时间30s,记录24小时(2880)
 # 说明: 默认情况下修改server和user就可以了。丢包率监测方向可以自定义,例如:CU = "www.facebook.com"。
 
 SERVER = "127.0.0.1"
@@ -17,6 +18,7 @@ CM = "cm.tz.cloudcpp.com"
 PROBEPORT = 80
 PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
 PING_PACKET_HISTORY_LEN = 100
+ONLINE_PACKET_HISTORY_LEN = 1440
 INTERVAL = 1
 
 import socket
@@ -319,7 +321,7 @@ def get_realtime_data():
 
 def _monitor_thread(name, host, interval, type):
     lostPacket = 0
-    packet_queue = Queue(maxsize=PING_PACKET_HISTORY_LEN)
+    packet_queue = Queue(maxsize=ONLINE_PACKET_HISTORY_LEN)
     while True:
         if name not in monitorServer.keys():
             monitorServer[name] = {
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index c2b3928..d468fcb 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -4,6 +4,7 @@
 # 依赖于psutil跨平台库
 # 版本:1.0.3, 支持Python版本:2.7 to 3.10
 # 支持操作系统: Linux, Windows, OSX, Sun Solaris, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures
+# ONLINE_PACKET_HISTORY_LEN, 探测间隔60s,记录24小时在线率(1440);探测时间60s,记录7天在线率(10080);探测时间30s,记录24小时(2880)
 # 说明: 默认情况下修改server和user就可以了。丢包率监测方向可以自定义,例如:CU = "www.facebook.com"。
 
 SERVER = "127.0.0.1"
@@ -18,6 +19,7 @@ CM = "cm.tz.cloudcpp.com"
 PROBEPORT = 80
 PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
 PING_PACKET_HISTORY_LEN = 100
+ONLINE_PACKET_HISTORY_LEN = 1440
 INTERVAL = 1
 
 import socket
@@ -307,7 +309,7 @@ def get_realtime_data():
 
 def _monitor_thread(name, host, interval, type):
     lostPacket = 0
-    packet_queue = Queue(maxsize=PING_PACKET_HISTORY_LEN)
+    packet_queue = Queue(maxsize=ONLINE_PACKET_HISTORY_LEN)
     while True:
         if name not in monitorServer.keys():
             monitorServer[name] = {

From 144987bcf119e5c6c462b9cd55e462cec0bbd259 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Sun, 21 Jan 2024 22:55:07 +0800
Subject: [PATCH 04/14] .2f

---
 clients/client-linux.py  | 2 +-
 clients/client-psutil.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 4d79bed..9f3e315 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -520,7 +520,7 @@ if __name__ == '__main__':
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
                 array['io_read'] = diskIO.get("read")
                 array['io_write'] = diskIO.get("write")
-                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100}%</code>" for k, v in monitorServer.items())
+                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.2f}%</code>" for k, v in monitorServer.items())
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
             raise
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index d468fcb..4e9c1f6 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -510,7 +510,7 @@ if __name__ == '__main__':
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
                 array['io_read'] = diskIO.get("read")
                 array['io_write'] = diskIO.get("write")
-                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100}%</code>" for k, v in monitorServer.items())
+                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.2f}%</code>" for k, v in monitorServer.items())
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
             raise

From 94708c588cab6ec3cb18df78b7d70220b5f7091c Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Mon, 22 Jan 2024 15:21:06 +0800
Subject: [PATCH 05/14] css resize

---
 web/css/dark.css  | 44 +++++++++++++++++++-------------------------
 web/css/light.css | 44 +++++++++++++++++++-------------------------
 2 files changed, 38 insertions(+), 50 deletions(-)

diff --git a/web/css/dark.css b/web/css/dark.css
index eea98f3..0d6881c 100644
--- a/web/css/dark.css
+++ b/web/css/dark.css
@@ -20,35 +20,29 @@ tr.odd.expandRow > :hover { background: #212e36 !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 110px; }
 #cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
-#ping { max-width: 110px; }
+#ping { max-width: 115px; }
 
 @media only screen and (max-width: 1200px) {
-        #server {
-                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-        }
+        #type                           { display:none; visibility:hidden; }
+        #location, tr td:nth-child(5)   { display:none; visibility:hidden; }
+        #uptime, tr td:nth-child(6)     { display:none; visibility:hidden; }
+        #ping, tr td:nth-child(13)      { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 720px) {
-        #server {
-                body                                                            { font-size: 10px; }
-                .content                                                        { padding: 0; }
-                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-        }
+        body                            { font-size: 10px; }
+        .content                        { padding: 0; }
+        #type                           { display:none; visibility:hidden; }
+        #location, tr td:nth-child(5)   { display:none; visibility:hidden; }
+        #uptime, tr td:nth-child(6)     { display:none; visibility:hidden; }
+        #ping, tr td:nth-child(13)      { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 620px) {
-        #server {
-                body                                                            { font-size: 10px; }
-                .content                                                        { padding: 0; }
-                #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-                #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-        }
+        body                            { font-size: 10px; }
+        .content                        { padding: 0; }
+        #month_traffic                  { display:none; visibility:hidden; }
+        #type                           { display:none; visibility:hidden; }
+        #location, tr td:nth-child(5)   { display:none; visibility:hidden; }
+        #uptime, tr td:nth-child(6)     { display:none; visibility:hidden; }
+        #traffic, tr td:nth-child(9)    { display:none; visibility:hidden; }
+        #ping, tr td:nth-child(13)      { display:none; visibility:hidden; }
 }
\ No newline at end of file
diff --git a/web/css/light.css b/web/css/light.css
index 37e7f77..811c8d0 100644
--- a/web/css/light.css
+++ b/web/css/light.css
@@ -17,35 +17,29 @@ tr.odd.expandRow > :hover { background: #FFF !important; }
 #month_traffic { min-width: 85px; max-width: 95px;}
 #network { min-width: 110px; }
 #cpu, #ram, #hdd { min-width: 45px; max-width: 90px; }
-#ping { max-width: 110px; }
+#ping { max-width: 115px; }
 
 @media only screen and (max-width: 1200px) {
-        #server {
-                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-        }
+        #type                           { display:none; visibility:hidden; }
+        #location, tr td:nth-child(5)   { display:none; visibility:hidden; }
+        #uptime, tr td:nth-child(6)     { display:none; visibility:hidden; }
+        #ping, tr td:nth-child(13)      { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 720px) {
-        #server {
-                body                                                            { font-size: 10px; }
-                .content                                                        { padding: 0; }
-                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-        }
+        body                            { font-size: 10px; }
+        .content                        { padding: 0; }
+        #type                           { display:none; visibility:hidden; }
+        #location, tr td:nth-child(5)   { display:none; visibility:hidden; }
+        #uptime, tr td:nth-child(6)     { display:none; visibility:hidden; }
+        #ping, tr td:nth-child(13)      { display:none; visibility:hidden; }
 }
 @media only screen and (max-width: 620px) {
-        #server {
-                body                                                            { font-size: 10px; }
-                .content                                                        { padding: 0; }
-                #month_traffic, tr td:nth-child(2)      { display:none; visibility:hidden; }
-                #type, tr td:nth-child(4)                       { display:none; visibility:hidden; }
-                #location, tr td:nth-child(5)           { display:none; visibility:hidden; }
-                #uptime, tr td:nth-child(6)                     { display:none; visibility:hidden; }
-                #traffic, tr td:nth-child(9)            { display:none; visibility:hidden; }
-                #ping, tr td:nth-child(13)          { display:none; visibility:hidden; }
-        }
+        body                            { font-size: 10px; }
+        .content                        { padding: 0; }
+        #month_traffic                  { display:none; visibility:hidden; }
+        #type                           { display:none; visibility:hidden; }
+        #location, tr td:nth-child(5)   { display:none; visibility:hidden; }
+        #uptime, tr td:nth-child(6)     { display:none; visibility:hidden; }
+        #traffic, tr td:nth-child(9)    { display:none; visibility:hidden; }
+        #ping, tr td:nth-child(13)      { display:none; visibility:hidden; }
 }
\ No newline at end of file

From 6ab046be51683c064bf38a97e5236e184d9b9378 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Mon, 22 Jan 2024 15:32:20 +0800
Subject: [PATCH 06/14] update

---
 clients/client-linux.py  | 4 ++--
 clients/client-psutil.py | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 9f3e315..8de029d 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -18,7 +18,7 @@ CM = "cm.tz.cloudcpp.com"
 PROBEPORT = 80
 PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
 PING_PACKET_HISTORY_LEN = 100
-ONLINE_PACKET_HISTORY_LEN = 1440
+ONLINE_PACKET_HISTORY_LEN = 10080
 INTERVAL = 1
 
 import socket
@@ -520,7 +520,7 @@ if __name__ == '__main__':
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
                 array['io_read'] = diskIO.get("read")
                 array['io_write'] = diskIO.get("write")
-                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.2f}%</code>" for k, v in monitorServer.items())
+                array['custom'] = "<br>".join(f"{k}\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.1f}%</code>" for k, v in monitorServer.items())
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
             raise
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index 4e9c1f6..4946a45 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -19,7 +19,7 @@ CM = "cm.tz.cloudcpp.com"
 PROBEPORT = 80
 PROBE_PROTOCOL_PREFER = "ipv4"  # ipv4, ipv6
 PING_PACKET_HISTORY_LEN = 100
-ONLINE_PACKET_HISTORY_LEN = 1440
+ONLINE_PACKET_HISTORY_LEN = 10080
 INTERVAL = 1
 
 import socket
@@ -510,7 +510,7 @@ if __name__ == '__main__':
                 array['tcp'], array['udp'], array['process'], array['thread'] = tupd()
                 array['io_read'] = diskIO.get("read")
                 array['io_write'] = diskIO.get("write")
-                array['custom'] = "<br>".join(f"<code>{k}</code>\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.2f}%</code>" for k, v in monitorServer.items())
+                array['custom'] = "<br>".join(f"{k}\\t解析: {v['dns_time']}\\t连接: {v['connect_time']}\\t下载: {v['download_time']}\\t在线率: <code>{v['online_rate']*100:.2f}%</code>" for k, v in monitorServer.items())
                 s.send(byte_str("update " + json.dumps(array) + "\n"))
         except KeyboardInterrupt:
             raise

From 03446b8f29a1d5e6d4998281439aed12b170d587 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Mon, 22 Jan 2024 15:34:44 +0800
Subject: [PATCH 07/14] update some for cl

---
 clients/client-linux.py  | 2 --
 clients/client-psutil.py | 2 --
 2 files changed, 4 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 8de029d..8ebb327 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -509,8 +509,6 @@ if __name__ == '__main__':
                 array['network_tx'] = netSpeed.get("nettx")
                 array['network_in'] = NET_IN
                 array['network_out'] = NET_OUT
-                # todo:兼容旧版本,下个版本删除ip_status
-                array['ip_status'] = True
                 array['ping_10010'] = lostRate.get('10010') * 100
                 array['ping_189'] = lostRate.get('189') * 100
                 array['ping_10086'] = lostRate.get('10086') * 100
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index 4946a45..278db93 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -499,8 +499,6 @@ if __name__ == '__main__':
                 array['network_tx'] = netSpeed.get("nettx")
                 array['network_in'] = NET_IN
                 array['network_out'] = NET_OUT
-                # todo:兼容旧版本,下个版本删除ip_status
-                array['ip_status'] = True
                 array['ping_10010'] = lostRate.get('10010') * 100
                 array['ping_189'] = lostRate.get('189') * 100
                 array['ping_10086'] = lostRate.get('10086') * 100

From 8dfc6d4718fe209ea9160401d0e6e489e60c3eca Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Mon, 22 Jan 2024 19:49:29 +0800
Subject: [PATCH 08/14] update

---
 clients/client-linux.py  | 1 +
 clients/client-psutil.py | 1 +
 2 files changed, 2 insertions(+)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 8ebb327..5a6bd1c 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -455,6 +455,7 @@ if __name__ == '__main__':
             if data.find("You are connecting via") < 0:
                 data = byte_str(s.recv(1024))
                 print(data)
+                monitorServer.clear()
                 for i in data.split('\n'):
                     if "monitor" in i and "type" in i and "{" in i and "}" in i:
                         jdata = json.loads(i[i.find("{"):i.find("}")+1])
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index 278db93..da871b8 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -444,6 +444,7 @@ if __name__ == '__main__':
             if data.find("You are connecting via") < 0:
                 data = byte_str(s.recv(1024))
                 print(data)
+                monitorServer.clear()
                 for i in data.split('\n'):
                     if "monitor" in i and "type" in i and "{" in i and "}" in i:
                         jdata = json.loads(i[i.find("{"):i.find("}")+1])

From eddf37c413667dc46b32d6845b33522ba6ae361f Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Tue, 23 Jan 2024 10:30:43 +0800
Subject: [PATCH 09/14] fix bug

---
 clients/client-linux.py  | 17 ++++++++++-------
 clients/client-psutil.py | 18 ++++++++++--------
 2 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/clients/client-linux.py b/clients/client-linux.py
index 5a6bd1c..fc65827 100755
--- a/clients/client-linux.py
+++ b/clients/client-linux.py
@@ -324,13 +324,7 @@ def _monitor_thread(name, host, interval, type):
     packet_queue = Queue(maxsize=ONLINE_PACKET_HISTORY_LEN)
     while True:
         if name not in monitorServer.keys():
-            monitorServer[name] = {
-                "type": type,
-                "dns_time": 0,
-                "connect_time": 0,
-                "download_time": 0,
-                "online_rate": 1
-            }
+            break
         if packet_queue.full():
             if packet_queue.get() == 0:
                 lostPacket -= 1
@@ -459,6 +453,13 @@ if __name__ == '__main__':
                 for i in data.split('\n'):
                     if "monitor" in i and "type" in i and "{" in i and "}" in i:
                         jdata = json.loads(i[i.find("{"):i.find("}")+1])
+                        monitorServer[jdata.get("name")] = {
+                            "type": jdata.get("type"),
+                            "dns_time": 0,
+                            "connect_time": 0,
+                            "download_time": 0,
+                            "online_rate": 1
+                        }
                         t = threading.Thread(
                             target=_monitor_thread,
                             kwargs={
@@ -524,11 +525,13 @@ if __name__ == '__main__':
         except KeyboardInterrupt:
             raise
         except socket.error:
+            monitorServer.clear()
             print("Disconnected...")
             if 's' in locals().keys():
                 del s
             time.sleep(3)
         except Exception as e:
+            monitorServer.clear()
             print("Caught Exception:", e)
             if 's' in locals().keys():
                 del s
diff --git a/clients/client-psutil.py b/clients/client-psutil.py
index da871b8..bcb8ea0 100755
--- a/clients/client-psutil.py
+++ b/clients/client-psutil.py
@@ -312,13 +312,7 @@ def _monitor_thread(name, host, interval, type):
     packet_queue = Queue(maxsize=ONLINE_PACKET_HISTORY_LEN)
     while True:
         if name not in monitorServer.keys():
-            monitorServer[name] = {
-                "type": type,
-                "dns_time": 0,
-                "connect_time": 0,
-                "download_time": 0,
-                "online_rate": 1
-            }
+            break
         if packet_queue.full():
             if packet_queue.get() == 0:
                 lostPacket -= 1
@@ -444,10 +438,16 @@ if __name__ == '__main__':
             if data.find("You are connecting via") < 0:
                 data = byte_str(s.recv(1024))
                 print(data)
-                monitorServer.clear()
                 for i in data.split('\n'):
                     if "monitor" in i and "type" in i and "{" in i and "}" in i:
                         jdata = json.loads(i[i.find("{"):i.find("}")+1])
+                        monitorServer[jdata.get("name")] = {
+                            "type": jdata.get("type"),
+                            "dns_time": 0,
+                            "connect_time": 0,
+                            "download_time": 0,
+                            "online_rate": 1
+                        }
                         t = threading.Thread(
                             target=_monitor_thread,
                             kwargs={
@@ -514,11 +514,13 @@ if __name__ == '__main__':
         except KeyboardInterrupt:
             raise
         except socket.error:
+            monitorServer.clear()
             print("Disconnected...")
             if 's' in locals().keys():
                 del s
             time.sleep(3)
         except Exception as e:
+            monitorServer.clear()
             print("Caught Exception:", e)
             if 's' in locals().keys():
                 del s

From 810f6a0d8a231ba676c46fc6e873d821a43102d9 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Tue, 23 Jan 2024 16:47:26 +0800
Subject: [PATCH 10/14] fix

---
 web/js/serverstatus.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/js/serverstatus.js b/web/js/serverstatus.js
index 529456c..02ca72b 100644
--- a/web/js/serverstatus.js
+++ b/web/js/serverstatus.js
@@ -91,7 +91,7 @@ function uptime() {
 			}
 			if (!MableRow.length) {
 				$("#monitors").append(
-					"<tr id=\"r" + i + "\" data-toggle=\"collapse\" data-target=\"#rt" + i + "\" class=\"accordion-toggle " + hack + "\">" +
+					"<tr id=\"r" + i + "\" data-target=\"#rt" + i + "\" class=\"accordion-toggle " + hack + "\">" +
 					"<td id=\"monitor_status\"><div class=\"progress\"><div style=\"width: 100%;\" class=\"progress-bar progress-bar-warning\"><small>加载中</small></div></div></td>" +
 					"<td id=\"monitor_node\">加载中</td>" +
 					"<td id=\"monitor_location\">加载中</td>" +

From fb3ecd179651e81d12095e8eafc6bfe5dd9d002f Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Tue, 23 Jan 2024 17:43:27 +0800
Subject: [PATCH 11/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AAtodo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md           | 22 +++++++++++++++-------
 server/config.json  |  2 +-
 server/src/main.cpp | 17 +++++++++++++++++
 3 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index a980f6c..64b72c1 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,7 @@ cd ServerStatus/server && make
 
 #### 二、修改配置文件         
 ```diff
-! watchdog rule 可以为任何已知字段的表达式       
+! watchdog rule 可以为任何已知字段的表达式。注意Exprtk库默认使用窄字符类型,中文等Unicode字符无法解析计算,todo等待修复       
 ! watchdog interval 最小通知间隔
 ! watchdog callback 可自定义为Post方法的URL,告警内容将拼接其后并发起回调 
 
@@ -89,13 +89,21 @@ cd ServerStatus/server && make
 			"location": "🇨🇳",
 			"password": "USER_DEFAULT_PASSWORD",
 			"monthstart": 1
-		},
+		}
+	],
+	"monitors": [
+		{
+			"name": "百度一下",
+			"host": "https://www.baidu.com",
+			"interval": 60,
+			"type": "https"
+		}
 	],
 	"watchdog":
 	[
 	        {
-			"name": "服务器负载高监控,排除内存大于32G物理机,同时排除俄勒冈机器",
-			"rule": "cpu>90&load_1>4&memory_total<33554432&name!='俄勒冈'",
+			"name": "服务器负载高监控,排除内存大于32G物理机,同时排除node1机器",
+			"rule": "cpu>90&load_1>4&memory_total<33554432&name!='node1'",
 			"interval": 600,
 			"callback": "https://yourSMSurl"
 		},
@@ -106,8 +114,8 @@ cd ServerStatus/server && make
                         "callback": "https://yourSMSurl"
                 },
                 {
-                        "name": "服务器宕机告警,排出俄勒冈,排除s02",
-                        "rule": "online4=0&online6=0&name!='俄勒冈'&username!='s02'",
+                        "name": "服务器宕机告警,排出node1,排除s02",
+                        "rule": "online4=0&online6=0&name!='node1'&username!='s02'",
                         "interval": 600,
                         "callback": "https://yourSMSurl"
                 },
@@ -124,7 +132,7 @@ cd ServerStatus/server && make
 			"callback": "https://yourSMSurl"
 		},
 		{
-			"name": "你可以组合任何已知字段的表达式",
+			"name": "你可以组合任何已知字段的表达式,注意Exprtk库目前不支持中文等Unicode字符",
 			"rule": "(hdd_used/hdd_total)*100>95",
 			"interval": 1800,
 			"callback": "https://yourSMSurl"
diff --git a/server/config.json b/server/config.json
index 29a4df4..aea2573 100644
--- a/server/config.json
+++ b/server/config.json
@@ -46,7 +46,7 @@
 			"type": "https"
 		},
 		{
-			"name": "主机交流",
+			"name": "502论坛",
 			"host": "https://www.hostloc.com",
 			"interval": 60,
 			"type": "https"
diff --git a/server/src/main.cpp b/server/src/main.cpp
index 2dc5c43..acada37 100644
--- a/server/src/main.cpp
+++ b/server/src/main.cpp
@@ -278,6 +278,23 @@ void CMain::WatchdogMessage(int ClientNetID, double load_1, double load_5, doubl
     int ID = 0;
     while (strcmp(Watchdog(ID)->m_aName, "NULL"))
     {
+//        Exprtk库默认使用窄字符类型,但可能会出现中文等Unicode字符无法正确解析的问题。
+//        todo: 为解决此问题,可以使用宽字符类型替换Exprtk库中默认的窄字符类型。
+//        #include <string>
+//        #include <vector>
+//        #include <exprtk.hpp>
+//        typedef exprtk::expression<wchar_t> expression_type;
+//        typedef exprtk::parser<wchar_t> parser_type;
+//        int main()
+//      {
+//                std::wstring expression_string = L"sin(x)";
+//                expression_type expression;
+//                parser_type parser;
+//                parser.compile(expression_string, expression);
+//                double x = 3.14;
+//                double result = expression.value();
+//                return 0;
+//       }
         typedef exprtk::symbol_table<double> symbol_table_t;
         typedef exprtk::expression<double>   expression_t;
         typedef exprtk::parser<double>       parser_t;

From c236aee5dc38a92cf54e133a1b96b7f4b8ea6520 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Tue, 23 Jan 2024 17:52:59 +0800
Subject: [PATCH 12/14] todo list

---
 README.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 64b72c1..c306d97 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,11 @@
 [![Python Support](https://img.shields.io/badge/python-3.6%2B%20-blue.svg)](https://github.com/cppla/ServerStatus)
 [![C++ Compiler](http://img.shields.io/badge/C++-GNU-blue.svg?style=flat&logo=cplusplus)](https://github.com/cppla/ServerStatus)
 [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/cppla/ServerStatus)
-[![Version](https://img.shields.io/badge/Version-Build%201.1.1-red)](https://github.com/cppla/ServerStatus)
+[![Version](https://img.shields.io/badge/Version-Build%201.1.2-red)](https://github.com/cppla/ServerStatus)
 
 ![Latest Version](http://dl.cpp.la/Archive/serverstatus_1.0.9.png)
 
-`Watchdog触发式告警,interval只是为了防止频繁收到报警信息造成的骚扰,并不是探测间隔。 同时为了防止海外机器闪断报警,也加入username、name、type等静态字符串参数的计算支持。`    
+`Watchdog触发式告警,interval只是为了防止频繁收到报警信息造成的骚扰,并不是探测间隔。 同时为了防止海外机器闪断报警,也加入username、name、type等静态字符串参数的计算支持。值得注意的是,Exprtk库默认使用窄字符类型,中文等Unicode字符无法解析计算,等待修复 `    
 
 # 目录:
 
@@ -67,7 +67,7 @@ cd ServerStatus/server && make
 
 #### 二、修改配置文件         
 ```diff
-! watchdog rule 可以为任何已知字段的表达式。注意Exprtk库默认使用窄字符类型,中文等Unicode字符无法解析计算,todo等待修复       
+! watchdog rule 可以为任何已知字段的表达式。注意Exprtk库默认使用窄字符类型,中文等Unicode字符无法解析计算,等待修复       
 ! watchdog interval 最小通知间隔
 ! watchdog callback 可自定义为Post方法的URL,告警内容将拼接其后并发起回调 
 
@@ -132,7 +132,7 @@ cd ServerStatus/server && make
 			"callback": "https://yourSMSurl"
 		},
 		{
-			"name": "你可以组合任何已知字段的表达式,注意Exprtk库目前不支持中文等Unicode字符",
+			"name": "你可以组合任何已知字段的表达式",
 			"rule": "(hdd_used/hdd_total)*100>95",
 			"interval": 1800,
 			"callback": "https://yourSMSurl"

From 1f7827cc211e9b482bd3b8cfcd65c7cb719ff968 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Tue, 23 Jan 2024 18:00:24 +0800
Subject: [PATCH 13/14] change

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index c306d97..d5e0968 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ cd ServerStatus/server && make
 	],
 	"monitors": [
 		{
-			"name": "百度一下",
+			"name": "你可以监测自己的网站以及MySQL、Redis",
 			"host": "https://www.baidu.com",
 			"interval": 60,
 			"type": "https"

From 052c75ef23c79557d87c393b2ff483110a0707f7 Mon Sep 17 00:00:00 2001
From: cppla <i@cpp.la>
Date: Tue, 23 Jan 2024 18:09:23 +0800
Subject: [PATCH 14/14] update

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index d5e0968..f44c254 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ cd ServerStatus/server && make
 	],
 	"monitors": [
 		{
-			"name": "你可以监测自己的网站以及MySQL、Redis",
+			"name": "监测网站以及MySQL、Redis,默认为七天在线率",
 			"host": "https://www.baidu.com",
 			"interval": 60,
 			"type": "https"