master
Evan Chen 2023-08-16 18:25:41 +08:00
commit d51ccc456b
6 changed files with 271 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
nwm
runtime

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module nwm
go 1.16

62
index.html Normal file
View File

@ -0,0 +1,62 @@
<div>
<canvas id="myChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@^3"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@^2"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script>
<script>
const ctx = document.getElementById('myChart');
const fails = [
{{.Data}}
]
new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Public Network',
data: fails.filter(el=>el.name=='Public'),
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgb(255, 99, 132)',
},
{
label: 'DSL',
data: fails.filter(el=>el.name=='DSL'),
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgb(75, 192, 192)'
},
{
label: 'Asus Router',
data: fails.filter(el=>el.name=='Asus'),
borderColor: 'rgb(255, 205, 86)',
backgroundColor: 'rgb(255, 205, 86)'
}
]
},
options: {
scales: {
x: {
type: 'time',
time: {
unit: 'second'
},
position: 'bottom'
},
y: {
type: 'category',
labels: ['', 'ok','failed','']
}
},
decimation: {
enabled: true,
algorithm: 'lttb'
}
}
});
</script>

184
main.go Normal file
View File

@ -0,0 +1,184 @@
package main
import (
"bytes"
_ "embed"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"text/template"
"time"
)
//go:embed index.html
var tmpl string
var report = template.Must(template.New("").Parse(tmpl))
var write_report chan bool
var interval = time.Second * 2
type Network struct {
Name string
IP string
OnFail string
}
var Networks = []*Network{
{
Name: "DSL",
IP: "220.135.85.67",
OnFail: "nmcli connection up DSL",
},
{
Name: "Asus",
IP: "192.168.50.48",
},
}
func main() {
write_report = make(chan bool, 1)
file, err := os.OpenFile("network.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModePerm)
if err != nil {
log.Panicln(err)
}
defer file.Close()
log.Println("starting network monitor")
go func() {
perr := false
derr := false
for reducer := 0; ; reducer++ {
now := time.Now().Format("2006-01-02 15:04:05")
// Public
go func(print bool) {
_, err = net.LookupIP("google.com")
if err != nil {
if !perr {
perr = true
log.Println(err)
}
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "failed", "Public")
fmt.Fprintf(file, "\n")
} else {
perr = false
if print {
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "ok", "Public")
fmt.Fprintf(file, "\n")
}
}
}(reducer == 0)
// devices
go func(print bool) {
for i := range Networks {
_, err = GetOutboundIP(Networks[i].IP + ":0")
if err != nil {
if !derr {
derr = true
log.Println(err)
}
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "failed", Networks[i].Name)
fmt.Fprintf(file, "\n")
// fmt.Fprintf(file, "%s\t%s\t%s\n", now, Networks[i].Name, "failed")
} else {
derr = false
if print {
fmt.Fprintf(file, `{"x": "%s", "y": "%s", "name": "%s"},`, now, "ok", Networks[i].Name)
fmt.Fprintf(file, "\n")
}
}
}
}(reducer == 0)
<-time.After(interval)
if reducer >= 9 {
reducer = -1
}
}
}()
go func() {
for {
<-time.After(time.Minute * 10)
WriteReport()
}
}()
go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, strings.TrimPrefix(r.URL.Path, "/"))
})
if err := http.ListenAndServe("0.0.0.0:1080", nil); err != nil {
log.Fatalln(err)
}
}()
wait := make(chan os.Signal, 1)
signal.Notify(wait, syscall.SIGINT, syscall.SIGTERM)
<-wait
WriteReport()
}
func WriteReport() {
select {
case write_report <- true:
default:
return
}
defer func() { <-write_report }()
f, err := os.OpenFile("report.html", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
log.Println(err)
return
}
defer f.Close()
data, err := os.ReadFile("network.log")
if err != nil {
log.Println(err)
return
}
content := &bytes.Buffer{}
if err = report.Execute(content, map[string]string{"Data": string(data)}); err != nil {
log.Println(err)
return
}
content.WriteTo(f)
}
// Get preferred outbound ip of this machine
func GetOutboundIP(addr string) (net.IP, error) {
iface, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
dialer := &net.Dialer{LocalAddr: iface}
conn, err := dialer.Dial("udp", "8.8.8.8:80")
if err != nil {
return nil, err
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP, nil
}

13
nwm.service Normal file
View File

@ -0,0 +1,13 @@
[Unit]
Description=Evan Network Monitor
[Service]
WorkingDirectory=/home/evan/Documents/network-monitor/runtime
ExecStart=/home/evan/Documents/network-monitor/nwm
Restart=always
StandardOutput=append:/home/evan/Documents/network-monitor/runtime/service.log
StandardError=append:/home/evan/Documents/network-monitor/runtime/service.log
[Install]
WantedBy=multi-user.target

7
restart.sh Executable file
View File

@ -0,0 +1,7 @@
sudo systemctl stop nwm
go build -o nwm main.go
sudo systemctl start nwm