X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/18a8614d8008ab352deb04ac3023a12eb4c59f76..1cf4131b9d1c09f413e79265ad2077e2c1c6bbe8:/cmd/mobius-hotline-server/main.go diff --git a/cmd/mobius-hotline-server/main.go b/cmd/mobius-hotline-server/main.go index f18ae9d..cf3c5a9 100644 --- a/cmd/mobius-hotline-server/main.go +++ b/cmd/mobius-hotline-server/main.go @@ -2,17 +2,25 @@ package main import ( "context" + "embed" + "encoding/json" "flag" "fmt" "github.com/jhalter/mobius/hotline" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "io" + "log" "math/rand" + "net/http" "os" "runtime" "time" ) +//go:embed mobius/config +var cfgTemplate embed.FS + const ( defaultPort = 5500 ) @@ -23,9 +31,12 @@ func main() { ctx, cancelRoot := context.WithCancel(context.Background()) basePort := flag.Int("bind", defaultPort, "Bind address and port") + statsPort := flag.String("stats-port", "", "Enable stats HTTP endpoint on address and port") configDir := flag.String("config", defaultConfigPath(), "Path to config root") version := flag.Bool("version", false, "print version and exit") logLevel := flag.String("log-level", "info", "Log level") + init := flag.Bool("init", false, "Populate the config dir with default configuration") + flag.Parse() if *version { @@ -44,21 +55,59 @@ func main() { defer func() { _ = l.Sync() }() logger := l.Sugar() + if *init { + if _, err := os.Stat(*configDir + "/config.yaml"); !os.IsNotExist(err) { + logger.Fatalw("Init failed. Existing config directory found: " + *configDir) + } + + if err := os.MkdirAll(*configDir, 0750); err != nil { + logger.Fatal(err) + } + + if err := copyDir("mobius/config", *configDir); err != nil { + logger.Fatal(err) + } + } + if _, err := os.Stat(*configDir); os.IsNotExist(err) { logger.Fatalw("Configuration directory not found", "path", configDir) } - hotline.FS = &hotline.OSFileStore{} - - srv, err := hotline.NewServer(*configDir, "", *basePort, logger) + srv, err := hotline.NewServer(*configDir, "", *basePort, logger, &hotline.OSFileStore{}) if err != nil { logger.Fatal(err) } + sh := statHandler{hlServer: srv} + if *statsPort != "" { + http.HandleFunc("/", sh.RenderStats) + + go func(srv *hotline.Server) { + // Use the default DefaultServeMux. + err = http.ListenAndServe(":"+*statsPort, nil) + if err != nil { + log.Fatal(err) + } + }(srv) + } + // Serve Hotline requests until program exit logger.Fatal(srv.ListenAndServe(ctx, cancelRoot)) } +type statHandler struct { + hlServer *hotline.Server +} + +func (sh *statHandler) RenderStats(w http.ResponseWriter, _ *http.Request) { + u, err := json.Marshal(sh.hlServer.Stats) + if err != nil { + panic(err) + } + + _, _ = io.WriteString(w, string(u)) +} + func newStdoutCore(level zapcore.Level) zapcore.Core { encoderCfg := zap.NewProductionEncoderConfig() encoderCfg.TimeKey = "timestamp" @@ -96,3 +145,53 @@ func defaultConfigPath() (cfgPath string) { return cfgPath } + +// TODO: Simplify this mess. Why is it so difficult to recursively copy a directory? +func copyDir(src, dst string) error { + entries, err := cfgTemplate.ReadDir(src) + if err != nil { + return err + } + for _, dirEntry := range entries { + if dirEntry.IsDir() { + if err := os.MkdirAll(dst+"/"+dirEntry.Name(), 0777); err != nil { + panic(err) + } + subdirEntries, _ := cfgTemplate.ReadDir(src + "/" + dirEntry.Name()) + for _, subDirEntry := range subdirEntries { + f, err := os.Create(dst + "/" + dirEntry.Name() + "/" + subDirEntry.Name()) + if err != nil { + return err + } + + srcFile, err := cfgTemplate.Open(src + "/" + dirEntry.Name() + "/" + subDirEntry.Name()) + if err != nil { + return err + } + _, err = io.Copy(f, srcFile) + if err != nil { + return err + } + f.Close() + } + } else { + f, err := os.Create(dst + "/" + dirEntry.Name()) + if err != nil { + return err + } + + srcFile, err := cfgTemplate.Open(src + "/" + dirEntry.Name()) + if err != nil { + return err + } + _, err = io.Copy(f, srcFile) + if err != nil { + return err + } + f.Close() + } + + } + + return nil +}