2025-02-01 19:38:24 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2025-02-02 10:56:30 +01:00
|
|
|
"context"
|
2025-02-01 19:59:27 +01:00
|
|
|
"encoding/json"
|
2025-02-01 19:38:24 +01:00
|
|
|
"fmt"
|
2025-02-01 19:59:27 +01:00
|
|
|
"net/http"
|
2025-02-01 19:38:24 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"git.tordarus.net/tordarus/channel"
|
|
|
|
"git.tordarus.net/tordarus/envvars"
|
2025-02-01 19:59:27 +01:00
|
|
|
"git.tordarus.net/tordarus/gmath"
|
2025-02-01 21:15:53 +01:00
|
|
|
omada "git.tordarus.net/tordarus/omada-api"
|
2025-02-01 19:38:24 +01:00
|
|
|
omadamodel "git.tordarus.net/tordarus/omada-api/model"
|
|
|
|
"git.tordarus.net/tordarus/slices"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ( // flags
|
2025-02-01 19:59:27 +01:00
|
|
|
FlagSiteNames = envvars.StringSlice("SITES", ",", []string{})
|
|
|
|
|
2025-02-01 19:38:24 +01:00
|
|
|
FlagOmadaURL = envvars.String("OMADA_URL", "http://localhost:8088")
|
|
|
|
FlagOmadaID = envvars.String("OMADA_ID", "")
|
|
|
|
FlagOmadaClientID = envvars.String("OMADA_CLIENT_ID", "")
|
|
|
|
FlagOmadaClientSecret = envvars.String("OMADA_CLIENT_SECRET", "")
|
|
|
|
FlagOmadaUsername = envvars.String("OMADA_USERNAME", "")
|
|
|
|
FlagOmadaPassword = envvars.String("OMADA_PASSWORD", "")
|
|
|
|
FlagRefreshInterval = envvars.Duration("REFRESH_INTERVAL", 30*time.Second)
|
2025-02-01 19:59:27 +01:00
|
|
|
|
|
|
|
FlagHttpInterface = envvars.String("HTTP_INTERFACE", "")
|
|
|
|
FlagHttpPort = envvars.Uint16("HTTP_PORT", 8080)
|
2025-02-01 19:38:24 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var MinRefreshInterval = 30 * time.Second
|
|
|
|
|
2025-02-01 19:59:27 +01:00
|
|
|
var CurrentTraffic = &TrafficStats{}
|
|
|
|
|
2025-02-01 19:38:24 +01:00
|
|
|
func main() {
|
|
|
|
FlagRefreshInterval = gmath.Max(FlagRefreshInterval, MinRefreshInterval)
|
|
|
|
|
2025-02-01 21:15:53 +01:00
|
|
|
api, err := omada.NewApi(omada.ApiConfig{
|
2025-02-01 19:38:24 +01:00
|
|
|
BasePath: FlagOmadaURL,
|
|
|
|
OmadaID: FlagOmadaID,
|
|
|
|
ClientID: FlagOmadaClientID,
|
|
|
|
ClientSecret: FlagOmadaClientSecret,
|
|
|
|
Username: FlagOmadaUsername,
|
|
|
|
Password: FlagOmadaPassword,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2025-02-02 10:59:05 +01:00
|
|
|
go func() {
|
|
|
|
if err := api.AutoRefresh(context.Background()); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}()
|
2025-02-02 10:56:30 +01:00
|
|
|
|
2025-02-01 19:59:27 +01:00
|
|
|
go func() {
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := json.NewEncoder(w).Encode(CurrentTraffic); err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
http.ListenAndServe(fmt.Sprintf("%s:%d", FlagHttpInterface, FlagHttpPort), nil)
|
|
|
|
}()
|
|
|
|
|
2025-02-01 19:38:24 +01:00
|
|
|
sites := slices.Filter(channel.ToSlice(api.GetSites()), FilterSitesByName(FlagSiteNames...))
|
|
|
|
|
|
|
|
ticker := time.NewTicker(FlagRefreshInterval)
|
|
|
|
|
|
|
|
CalculateSiteTraffic(api, sites, 0)
|
|
|
|
|
|
|
|
lastTick := time.Now()
|
|
|
|
for now := range ticker.C {
|
2025-02-01 19:59:27 +01:00
|
|
|
CurrentTraffic = CalculateSiteTraffic(api, sites, now.Sub(lastTick))
|
2025-02-01 19:38:24 +01:00
|
|
|
lastTick = now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-01 21:15:53 +01:00
|
|
|
func CalculateSiteTraffic(api *omada.Api, sites []*omadamodel.Site, duration time.Duration) *TrafficStats {
|
2025-02-01 19:38:24 +01:00
|
|
|
siteTraffics := map[string]*SiteTraffic{}
|
|
|
|
|
|
|
|
for _, site := range sites {
|
|
|
|
trafficByClient := map[string]TrafficRate{}
|
|
|
|
|
|
|
|
clients := channel.ToSlice(api.GetClients(site.ID))
|
|
|
|
for _, client := range clients {
|
|
|
|
traffic := CalculateClientTraffic(site, client, duration)
|
|
|
|
trafficByClient[client.Name] = traffic
|
|
|
|
}
|
|
|
|
|
|
|
|
siteTraffics[site.Name] = NewSiteTraffic(trafficByClient)
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewTrafficStats(siteTraffics)
|
|
|
|
}
|
|
|
|
|
|
|
|
func FilterSitesByName(allowedSiteNames ...string) func(site *omadamodel.Site) bool {
|
|
|
|
if len(allowedSiteNames) == 0 {
|
|
|
|
return func(site *omadamodel.Site) bool { return true }
|
|
|
|
}
|
|
|
|
|
|
|
|
siteNames := slices.ToStructMap(allowedSiteNames)
|
|
|
|
|
|
|
|
return func(site *omadamodel.Site) bool {
|
|
|
|
_, ok := siteNames[site.Name]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
}
|