GPT generated - need to review
This commit is contained in:
parent
dfccac1752
commit
f23e0bc362
75
healthchecker.go
Normal file
75
healthchecker.go
Normal file
@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var services = []string{
|
||||
"https://git.aaronic.cc",
|
||||
"https://portainer.aaronic.cc",
|
||||
"https://jellyfin.aaronic.cc",
|
||||
"https://dnd.aaronic.cc",
|
||||
}
|
||||
|
||||
type HealthChecker struct {
|
||||
status map[string]ServiceStatus
|
||||
mu sync.RWMutex
|
||||
client *http.Client
|
||||
sse *SSEBroker
|
||||
}
|
||||
|
||||
func NewHealthChecker() *HealthChecker {
|
||||
return &HealthChecker{
|
||||
status: make(map[string]ServiceStatus),
|
||||
client: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
sse: NewSSEBroker(),
|
||||
}
|
||||
}
|
||||
|
||||
func (hc *HealthChecker) CheckAll() {
|
||||
for _, service := range services {
|
||||
go hc.checkService(service)
|
||||
}
|
||||
}
|
||||
|
||||
func (hc *HealthChecker) checkService(url string) {
|
||||
resp, err := hc.client.Get(url)
|
||||
status := ServiceStatus{
|
||||
URL: url,
|
||||
CheckedAt: time.Now(),
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Error checking %s: %s", url, err)
|
||||
status.IsUp = false
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
status.StatusCode = resp.StatusCode
|
||||
status.IsUp = resp.StatusCode >= 200 && resp.StatusCode < 300
|
||||
log.Printf("%s: %d", url, resp.StatusCode)
|
||||
}
|
||||
|
||||
hc.mu.Lock()
|
||||
hc.status[url] = status
|
||||
hc.mu.Unlock()
|
||||
|
||||
data, _ := json.Marshal(status)
|
||||
hc.sse.Broadcast(string(data))
|
||||
}
|
||||
|
||||
func (hc *HealthChecker) GetStatuses() []ServiceStatus {
|
||||
hc.mu.RLock()
|
||||
defer hc.mu.RUnlock()
|
||||
|
||||
results := make([]ServiceStatus, 0, len(hc.status))
|
||||
for _, status := range hc.status {
|
||||
results = append(results, status)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
42
main.go
42
main.go
@ -1,25 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
//TIP To run your code, right-click the code and select <b>Run</b>. Alternatively, click
|
||||
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.
|
||||
|
||||
func main() {
|
||||
//TIP Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined or highlighted text
|
||||
// to see how GoLand suggests fixing it.
|
||||
s := "gopher"
|
||||
fmt.Println("Hello and welcome, %s!", s)
|
||||
sseBroker := NewSSEBroker()
|
||||
checker := NewHealthChecker()
|
||||
|
||||
for i := 1; i <= 5; i++ {
|
||||
//TIP You can try debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>. To start your debugging session,
|
||||
// right-click your code in the editor and select the <b>Debug</b> option.
|
||||
fmt.Println("i =", 100/i)
|
||||
}
|
||||
go func() {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
checker.CheckAll()
|
||||
<-ticker.C
|
||||
}
|
||||
}()
|
||||
|
||||
//TIP See GoLand help at <a href="https://www.jetbrains.com/help/go/">jetbrains.com/help/go/</a>.
|
||||
// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.
|
||||
http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
|
||||
statuses := checker.GetStatuses()
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(statuses)
|
||||
})
|
||||
|
||||
http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
|
||||
sseBroker.Handler(w, r)
|
||||
})
|
||||
|
||||
log.Println("Serving on :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
62
sse.go
Normal file
62
sse.go
Normal file
@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type SSEBroker struct {
|
||||
clients map[chan string]struct{}
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewSSEBroker() *SSEBroker {
|
||||
return &SSEBroker{
|
||||
clients: make(map[chan string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SSEBroker) AddClient(c chan string) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.clients[c] = struct{}{}
|
||||
}
|
||||
|
||||
func (b *SSEBroker) RemoveClient(c chan string) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
delete(b.clients, c)
|
||||
}
|
||||
|
||||
func (b *SSEBroker) Broadcast(msg string) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
for c := range b.clients {
|
||||
c <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SSEBroker) Handler(w http.ResponseWriter, r *http.Request) {
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(w, "Streaming not supported!", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
messageChannel := make(chan string)
|
||||
|
||||
b.AddClient(messageChannel)
|
||||
defer b.RemoveClient(messageChannel)
|
||||
for {
|
||||
msg, open := <-messageChannel
|
||||
if !open {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "data: %s\n\n", msg)
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user