195 lines
5.6 KiB
Go
195 lines
5.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"git.oblat.lv/alex/triggerssmith/api"
|
|
application "git.oblat.lv/alex/triggerssmith/internal/app"
|
|
"git.oblat.lv/alex/triggerssmith/internal/config"
|
|
"git.oblat.lv/alex/triggerssmith/internal/server"
|
|
"git.oblat.lv/alex/triggerssmith/internal/vars"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var optsServeCmd = struct {
|
|
ConfigPath *string
|
|
Debug *bool
|
|
HideGreetings *bool
|
|
}{}
|
|
|
|
// // simple middleware for request logging
|
|
// func loggingMiddleware(next http.Handler) http.Handler {
|
|
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// start := time.Now()
|
|
|
|
// slog.Info("HTTP request",
|
|
// slog.String("method", r.Method),
|
|
// slog.String("path", r.URL.Path),
|
|
// slog.String("remote", r.RemoteAddr),
|
|
// )
|
|
|
|
// next.ServeHTTP(w, r)
|
|
|
|
// slog.Debug("HTTP request finished",
|
|
// slog.String("method", r.Method),
|
|
// slog.String("path", r.URL.Path),
|
|
// slog.Duration("latency", time.Since(start)),
|
|
// )
|
|
// })
|
|
// }
|
|
|
|
func writePID(path string) error {
|
|
dir := filepath.Dir(path)
|
|
err := os.MkdirAll(dir, 0644)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
pid := os.Getpid()
|
|
|
|
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
_, err = fmt.Fprintf(f, "%d\n", pid)
|
|
return err
|
|
}
|
|
|
|
var serveCmd = &cobra.Command{
|
|
Use: "serve",
|
|
Short: "Start the server",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
text := fmt.Sprintf(`
|
|
_______ _____
|
|
|__ __/ ____|
|
|
| | | (___
|
|
| | \___ \
|
|
| | ____) |
|
|
|_| |_____/
|
|
|
|
TriggerSmith - v%s
|
|
`, vars.Version)
|
|
if !*optsServeCmd.HideGreetings {
|
|
fmt.Println(text)
|
|
}
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
slog.Error("Application panicked", slog.Any("error", r))
|
|
os.Exit(-1)
|
|
}
|
|
}()
|
|
|
|
// configure logger
|
|
if *optsServeCmd.Debug {
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})))
|
|
} else {
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelInfo})))
|
|
}
|
|
|
|
pid := os.Getpid()
|
|
slog.Debug("Starting server", slog.Int("pid", pid))
|
|
if err := writePID(vars.PID_PATH); err != nil {
|
|
panic(err)
|
|
}
|
|
slog.Debug("created pid file", slog.String("path", vars.PID_PATH))
|
|
defer os.Remove(vars.PID_PATH)
|
|
|
|
// load config
|
|
slog.Debug("Reading configuration", slog.String("path", *optsServeCmd.ConfigPath))
|
|
cfg, err := config.LoadConfig(*optsServeCmd.ConfigPath)
|
|
if err != nil {
|
|
slog.Error("Failed to load configuration", slog.String("path", *optsServeCmd.ConfigPath), slog.String("error", err.Error()))
|
|
return
|
|
}
|
|
slog.Debug("Configuration loaded", slog.Any("config", cfg))
|
|
|
|
// init app
|
|
app, err := application.NewApp()
|
|
if err != application.ErrNilPointerWarn && err != nil {
|
|
slog.Error("Failed to create app instance", slog.String("error", err.Error()))
|
|
return
|
|
}
|
|
app.LoadConfiguration(cfg)
|
|
|
|
srv := app.Server()
|
|
//mux := http.NewServeMux()
|
|
|
|
// static files
|
|
// staticPath := cfg.Server.StaticFilesPath
|
|
// slog.Debug("Setting up static file server", slog.String("path", staticPath))
|
|
// fs := http.FileServer(http.Dir(staticPath))
|
|
// mux.Handle("/static/", http.StripPrefix("/static/", fs))
|
|
// handler := loggingMiddleware(mux)
|
|
|
|
router := api.NewRouter(cfg)
|
|
|
|
srv.SetHandler(router.RouteHandler())
|
|
srv.Init()
|
|
|
|
var addr = net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))
|
|
slog.Debug("Binding listener", slog.String("address", addr))
|
|
err = srv.Start(addr)
|
|
if err != nil {
|
|
slog.Error("Failed to start server", slog.String("error", err.Error()))
|
|
return
|
|
} else {
|
|
slog.Info("Server started", slog.String("address", net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))))
|
|
}
|
|
|
|
sigch := make(chan os.Signal, 1)
|
|
signal.Notify(sigch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
|
for true {
|
|
sig := <-sigch
|
|
slog.Debug("got signal", slog.Any("os.Signal", sig))
|
|
switch sig {
|
|
case syscall.SIGHUP:
|
|
if err := config.ReloadConfig(cfg); err != nil {
|
|
slog.Error("Failed to reload configuration", slog.String("error", err.Error()))
|
|
} else {
|
|
slog.Info("Configuration reloaded")
|
|
var addr = net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))
|
|
slog.Debug("New configuration", slog.Any("config", cfg))
|
|
err = srv.Reload(addr)
|
|
if err != nil {
|
|
slog.Error("Failed to restart server with new configuration", slog.String("error", err.Error()))
|
|
}
|
|
}
|
|
case syscall.SIGINT:
|
|
slog.Info("Stopping server by SIGINT")
|
|
os.Remove(vars.PID_PATH)
|
|
_ = server.StopAll()
|
|
//err := srv.Stop()
|
|
// if err != nil {
|
|
// slog.Error("Failed to stop server", slog.String("err", err.Error()))
|
|
// os.Exit(1)
|
|
// }
|
|
return
|
|
case syscall.SIGTERM:
|
|
slog.Info("Stopping server by SIGTERM")
|
|
os.Remove(vars.PID_PATH)
|
|
_ = server.StopAll()
|
|
//err := srv.Stop()
|
|
// if err != nil {
|
|
// slog.Error("Failed to stop server", slog.String("err", err.Error()))
|
|
// os.Exit(1)
|
|
// }
|
|
return
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
optsServeCmd.Debug = serveCmd.Flags().BoolP("debug", "d", false, "Enable debug logs")
|
|
optsServeCmd.ConfigPath = serveCmd.Flags().StringP("config", "c", "config.yaml", "Path to configuration file")
|
|
optsServeCmd.HideGreetings = serveCmd.Flags().BoolP("hide-greetings", "g", false, "Hide the welcome message and version when starting the server")
|
|
rootCmd.AddCommand(serveCmd)
|
|
}
|