diff --git a/assets/status-down.svg b/assets/status-down.svg
new file mode 100644
index 0000000..94c37a4
--- /dev/null
+++ b/assets/status-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/status-unknown.svg b/assets/status-unknown.svg
new file mode 100644
index 0000000..6da6a26
--- /dev/null
+++ b/assets/status-unknown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/status-up.svg b/assets/status-up.svg
new file mode 100644
index 0000000..b3c4b11
--- /dev/null
+++ b/assets/status-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/config.go b/config.go
index 30cbbea..1b17844 100644
--- a/config.go
+++ b/config.go
@@ -14,6 +14,8 @@ import (
)
func reloadConfig() {
+ log.Info("Loading configuration...")
+
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
@@ -55,7 +57,15 @@ func reloadConfig() {
mirrors["default"] = append(mirrors["NA"], mirrors["EU"]...)
- mirrorMap = mirrors
+ regionMap = mirrors
+
+ hosts := make(map[string]*Server)
+
+ for _, server := range servers {
+ hosts[server.Host] = server
+ }
+
+ hostMap = hosts
// Check top choices size
if topChoices > len(servers) {
diff --git a/http.go b/http.go
index 21391f9..f7f6c86 100644
--- a/http.go
+++ b/http.go
@@ -17,29 +17,6 @@ func statusHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}
-func legacyMirrorsHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
-
- mirrorOutput := make(map[string][]string)
-
- for region, mirrors := range mirrorMap {
- list := make([]string, len(mirrors))
-
- for i, mirror := range mirrors {
- list[i] = r.URL.Scheme + "://" + mirror.Host + "/" + strings.TrimLeft(mirror.Path, "/")
- }
-
- mirrorOutput[region] = list
- }
-
- json.NewEncoder(w).Encode(mirrorOutput)
-}
-
-func mirrorsHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(servers)
-}
-
func redirectHandler(w http.ResponseWriter, r *http.Request) {
ipStr, _, err := net.SplitHostPort(r.RemoteAddr)
@@ -67,7 +44,7 @@ func redirectHandler(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(r.URL.Path, "/")
// region = parts[2]
- if mirrors, ok := mirrorMap[parts[2]]; ok {
+ if mirrors, ok := regionMap[parts[2]]; ok {
choices := make([]randutil.Choice, len(mirrors))
for i, item := range mirrors {
diff --git a/main.go b/main.go
index 7dad8f2..42f9910 100644
--- a/main.go
+++ b/main.go
@@ -18,11 +18,12 @@ import (
)
var (
- db *maxminddb.Reader
- servers ServerList
- mirrorMap map[string][]*Server
-
- dlMap map[string]string
+ db *maxminddb.Reader
+ servers ServerList
+ regionMap map[string][]*Server
+ hostMap map[string]*Server
+ dlMap map[string]string
+ topChoices int
redirectsServed = promauto.NewCounter(prometheus.CounterOpts{
Name: "armbian_router_redirects",
@@ -35,8 +36,6 @@ var (
})
serverCache *lru.Cache
-
- topChoices int
)
type LocationLookup struct {
@@ -114,6 +113,7 @@ func main() {
r.Head("/status", statusHandler)
r.Get("/status", statusHandler)
r.Get("/mirrors", legacyMirrorsHandler)
+ r.Get("/mirrors/{server}.svg", mirrorStatusHandler)
r.Get("/mirrors.json", mirrorsHandler)
r.Post("/reload", reloadHandler)
r.Get("/dl_map", dlMapHandler)
diff --git a/mirrors.go b/mirrors.go
new file mode 100644
index 0000000..ed92411
--- /dev/null
+++ b/mirrors.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+ _ "embed"
+ "encoding/json"
+ "github.com/go-chi/chi/v5"
+ "net/http"
+ "strings"
+)
+
+func legacyMirrorsHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+
+ mirrorOutput := make(map[string][]string)
+
+ for region, mirrors := range regionMap {
+ list := make([]string, len(mirrors))
+
+ for i, mirror := range mirrors {
+ list[i] = r.URL.Scheme + "://" + mirror.Host + "/" + strings.TrimLeft(mirror.Path, "/")
+ }
+
+ mirrorOutput[region] = list
+ }
+
+ json.NewEncoder(w).Encode(mirrorOutput)
+}
+
+func mirrorsHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(servers)
+}
+
+var (
+ //go:embed assets/status-up.svg
+ statusUp []byte
+
+ //go:embed assets/status-down.svg
+ statusDown []byte
+
+ //go:embed assets/status-unknown.svg
+ statusUnknown []byte
+)
+
+func mirrorStatusHandler(w http.ResponseWriter, r *http.Request) {
+ serverHost := chi.URLParam(r, "server")
+
+ w.Header().Set("Content-Type", "image/svg+xml;charset=utf-8")
+
+ if serverHost == "" {
+ w.Write(statusUnknown)
+ return
+ }
+
+ serverHost = strings.Replace(serverHost, "_", ".", -1)
+
+ server, ok := hostMap[serverHost]
+
+ if !ok {
+ w.Write(statusUnknown)
+ return
+ }
+
+ if server.Available {
+ w.Write(statusUp)
+ } else {
+ w.Write(statusDown)
+ }
+}
diff --git a/servers.go b/servers.go
index 5bca3aa..b76553f 100644
--- a/servers.go
+++ b/servers.go
@@ -16,19 +16,20 @@ import (
var (
checkClient = &http.Client{
- Timeout: 10 * time.Second,
+ Timeout: 20 * time.Second,
}
)
type Server struct {
- Available bool `json:"available"`
- Host string `json:"host"`
- Path string `json:"path"`
- Latitude float64 `json:"latitude"`
- Longitude float64 `json:"longitude"`
- Weight int `json:"weight"`
- Continent string `json:"continent"`
- Redirects prometheus.Counter `json:"-"`
+ Available bool `json:"available"`
+ Host string `json:"host"`
+ Path string `json:"path"`
+ Latitude float64 `json:"latitude"`
+ Longitude float64 `json:"longitude"`
+ Weight int `json:"weight"`
+ Continent string `json:"continent"`
+ Redirects prometheus.Counter `json:"-"`
+ LastChange time.Time `json:"lastChange"`
}
func (server *Server) checkStatus() {
@@ -55,6 +56,7 @@ func (server *Server) checkStatus() {
}).Info("Server went offline")
server.Available = false
+ server.LastChange = time.Now()
} else {
log.WithFields(log.Fields{
"server": server.Host,
@@ -72,6 +74,7 @@ func (server *Server) checkStatus() {
if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusMovedPermanently || res.StatusCode == http.StatusFound || res.StatusCode == http.StatusNotFound {
if !server.Available {
server.Available = true
+ server.LastChange = time.Now()
log.WithFields(responseFields).Info("Server is online")
}
} else {
@@ -80,6 +83,7 @@ func (server *Server) checkStatus() {
if server.Available {
log.WithFields(responseFields).Info("Server went offline")
server.Available = false
+ server.LastChange = time.Now()
}
}
}