mirror of
https://github.com//cppla/ServerStatus
synced 2026-08-06 08:13:56 +08:00
139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
var opts Options
|
|
var legacyBind string
|
|
var legacyPort int
|
|
var printVersion bool
|
|
|
|
flag.StringVar(&opts.ConfigPath, "config", envOr("CONFIG_PATH", "config.json"), "configuration file")
|
|
flag.StringVar(&opts.ConfigPath, "c", envOr("CONFIG_PATH", "config.json"), "configuration file (shorthand)")
|
|
flag.StringVar(&opts.StatsPath, "stats", os.Getenv("STATS_PATH"), "persistent stats JSON file")
|
|
flag.StringVar(&opts.WebDir, "web-dir", envOr("WEB_DIR", "../web"), "WebUI directory")
|
|
flag.StringVar(&opts.WebDir, "d", envOr("WEB_DIR", "../web"), "WebUI directory (shorthand)")
|
|
flag.StringVar(&opts.HTTPAddr, "http", envOr("HTTP_ADDR", ":8080"), "HTTP listen address")
|
|
flag.StringVar(&opts.AgentAddr, "agent", envOr("AGENT_ADDR", ":35601"), "agent TCP listen address")
|
|
flag.StringVar(&legacyBind, "bind", "", "agent bind address (legacy compatibility)")
|
|
flag.StringVar(&legacyBind, "b", "", "agent bind address (legacy shorthand)")
|
|
flag.IntVar(&legacyPort, "port", 0, "agent TCP port (legacy compatibility)")
|
|
flag.IntVar(&legacyPort, "p", 0, "agent TCP port (legacy shorthand)")
|
|
flag.BoolVar(&opts.Verbose, "verbose", envBool("VERBOSE", false), "verbose HTTP logging")
|
|
flag.BoolVar(&opts.Verbose, "v", envBool("VERBOSE", false), "verbose HTTP logging (shorthand)")
|
|
flag.BoolVar(&printVersion, "version", false, "print version and exit")
|
|
flag.Parse()
|
|
|
|
if printVersion {
|
|
fmt.Printf("serverstatus %s commit=%s built=%s\n", version, commit, buildTime)
|
|
return
|
|
}
|
|
if opts.StatsPath == "" {
|
|
opts.StatsPath = filepath.Join(opts.WebDir, "json", "stats.json")
|
|
}
|
|
if legacyPort != 0 || legacyBind != "" {
|
|
if legacyPort == 0 {
|
|
legacyPort = 35601
|
|
}
|
|
opts.AgentAddr = net.JoinHostPort(legacyBind, strconv.Itoa(legacyPort))
|
|
}
|
|
opts.AdminToken = os.Getenv("ADMIN_TOKEN")
|
|
opts.CORSOrigin = os.Getenv("ADMIN_CORS_ORIGIN")
|
|
opts.InsecureCallbackTLS = envBool("INSECURE_CALLBACK_TLS", false)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(opts.StatsPath), 0o755); err != nil {
|
|
fatalf("create stats directory: %v", err)
|
|
}
|
|
app, err := NewApp(opts)
|
|
if err != nil {
|
|
fatalf("start: %v", err)
|
|
}
|
|
app.StartBackground()
|
|
|
|
httpServer := app.HTTPServer()
|
|
agentServer := NewAgentServer(app)
|
|
errorsChannel := make(chan error, 2)
|
|
go func() {
|
|
app.logger.Printf("HTTP listening on %s", opts.HTTPAddr)
|
|
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
errorsChannel <- fmt.Errorf("HTTP server: %w", err)
|
|
}
|
|
}()
|
|
go func() {
|
|
if err := agentServer.ListenAndServe(); err != nil {
|
|
errorsChannel <- fmt.Errorf("agent server: %w", err)
|
|
}
|
|
}()
|
|
|
|
reloadSignals := make(chan os.Signal, 1)
|
|
stopSignals := make(chan os.Signal, 1)
|
|
signal.Notify(reloadSignals, syscall.SIGHUP)
|
|
signal.Notify(stopSignals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
|
defer signal.Stop(reloadSignals)
|
|
defer signal.Stop(stopSignals)
|
|
|
|
go func() {
|
|
for range reloadSignals {
|
|
if apiErr := app.ReloadConfig(); apiErr != nil {
|
|
app.logger.Printf("reload config: %s", apiErr.Message)
|
|
continue
|
|
}
|
|
app.logger.Printf("configuration reloaded; generation=%d", app.generation.Load())
|
|
}
|
|
}()
|
|
|
|
var fatalErr error
|
|
select {
|
|
case signalValue := <-stopSignals:
|
|
app.logger.Printf("received %s; shutting down", signalValue)
|
|
case fatalErr = <-errorsChannel:
|
|
app.logger.Printf("fatal: %v", fatalErr)
|
|
}
|
|
app.cancel()
|
|
shutdownContext, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
_ = httpServer.Shutdown(shutdownContext)
|
|
cancel()
|
|
app.Close()
|
|
if fatalErr != nil {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func envOr(name, fallback string) string {
|
|
if value := strings.TrimSpace(os.Getenv(name)); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func envBool(name string, fallback bool) bool {
|
|
value := strings.TrimSpace(os.Getenv(name))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
parsed, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func fatalf(format string, args ...any) {
|
|
fmt.Fprintf(os.Stderr, "serverstatus: "+format+"\n", args...)
|
|
os.Exit(1)
|
|
}
|