add rate limiting, CSRF, newsletter, auto-checker, /uses and /projects pages
This commit is contained in:
88
internal/checker/checker.go
Normal file
88
internal/checker/checker.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Package checker periodically HTTP-checks services and updates status.json.
|
||||
package checker
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"ridgwaysystems.org/website/internal/status"
|
||||
)
|
||||
|
||||
// Start launches the background checker. It checks services every interval
|
||||
// and updates status.json in dataDir. Services without a CheckURL are skipped.
|
||||
func Start(dataDir string, interval time.Duration) {
|
||||
go run(dataDir, interval)
|
||||
}
|
||||
|
||||
func run(dataDir string, interval time.Duration) {
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) >= 3 {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
path := filepath.Join(dataDir, "status.json")
|
||||
|
||||
for {
|
||||
check(client, path)
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}
|
||||
|
||||
func check(client *http.Client, path string) {
|
||||
page, err := status.Load(path)
|
||||
if err != nil {
|
||||
log.Printf("checker: load %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
|
||||
changed := false
|
||||
for i, svc := range page.Services {
|
||||
if svc.CheckURL == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
prev := svc.Status
|
||||
newStatus := probe(client, svc.CheckURL)
|
||||
if newStatus != prev {
|
||||
log.Printf("checker: %s %s → %s", svc.Name, prev, newStatus)
|
||||
page.Services[i].Status = newStatus
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
if err := status.Save(path, page); err != nil {
|
||||
log.Printf("checker: save %s: %v", path, err)
|
||||
}
|
||||
} else {
|
||||
// Still update last_checked timestamp so the status page shows freshness
|
||||
page.LastChecked = time.Now().UTC()
|
||||
if err := status.Save(path, page); err != nil {
|
||||
log.Printf("checker: save %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func probe(client *http.Client, url string) string {
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return "down"
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
switch {
|
||||
case resp.StatusCode < 400:
|
||||
return "up"
|
||||
case resp.StatusCode >= 500:
|
||||
return "down"
|
||||
default:
|
||||
// 4xx could mean the service is up but the URL is wrong; treat as degraded
|
||||
return "degraded"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user