tsvnstat/main.go

145 lines
3.4 KiB
Go

package main
import (
_ "embed"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"os/exec"
"sort"
"strings"
"time"
"tailscale.com/tsnet"
)
//go:embed style.css
var style string
func runCmd(cmd string, dir string, args ...string) {
genCmd := exec.Command(cmd, args...)
genCmd.Dir = dir
out, err := genCmd.Output()
if err != nil {
log.Println(string(out), err)
}
}
func main() {
tmpDir, err := os.MkdirTemp("", "tsvnstat")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
name := flag.String("name", "", "name of service")
dir := flag.String("dir", tmpDir, "directory containing vnstat images")
key := flag.String("key", "", "path to file containing the api key")
vnstati := flag.String("vnstati", "/bin/vnstati", "path to vnstati")
flag.Parse()
s := &tsnet.Server{
Hostname: *name,
}
if *key != "" {
keyData, err := os.ReadFile(*key)
if err != nil {
log.Fatal(err)
}
s.AuthKey = string(keyData)
}
ln, err := s.Listen("tcp", ":80")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
host, err := os.Hostname()
if err != nil {
log.Fatal(err)
}
go func() {
for {
ifaces, err := net.Interfaces()
if err != nil {
log.Fatal("can't get interfaces...", err)
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue
}
runCmd(*vnstati, *dir, "--style", "1", "-L", "-s", "-o", fmt.Sprintf("%s-s.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "--fivegraph", "576", "218", "-o", fmt.Sprintf("%s-5g.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "-hg", "-o", fmt.Sprintf("%s-hg.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "-h", "24", "-o", fmt.Sprintf("%s-h.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "-d", "30", "-o", fmt.Sprintf("%s-d.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "-t", "10", "-o", fmt.Sprintf("%s-t.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "-m", "12", "-o", fmt.Sprintf("%s-m.png", iface.Name), iface.Name)
runCmd(*vnstati, *dir, "--style", "1", "-L", "-y", "5", "-o", fmt.Sprintf("%s-y.png", iface.Name), iface.Name)
}
time.Sleep(5 * time.Minute)
}
}()
mux := http.NewServeMux()
fileServer := http.FileServer(http.Dir(*dir))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fileServer.ServeHTTP(w, r)
})
mux.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) {
images, err := os.ReadDir(*dir)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
fmt.Fprintf(w, `<html>
<head>
<title>%s</title>
<style>%s</style>
</head>
<body>
<h1>vnstat for %s</h1>`, host, style, host)
sort.Slice(images, func(i, j int) bool {
return images[i].Name() < images[j].Name()
})
prefix := ""
oldPrefix := ""
for _, img := range images {
in := img.Name()
imgPrefix := strings.Split(in, "-")[0]
headImg := fmt.Sprintf("%s-s.png", imgPrefix)
if prefix != imgPrefix {
if oldPrefix != prefix {
fmt.Fprintf(w, "</p></details>")
}
fmt.Fprintf(w, "<details><summary><img src=%q /></summary><p>", headImg)
prefix = imgPrefix
}
if in != headImg {
fmt.Fprintf(w, "<img src=%q /><br />", in)
}
}
})
hs := &http.Server{
Handler: mux,
}
log.Panic(hs.Serve(ln))
}