package cmd import ( "fmt" "log/slog" "net" "net/http" "time" application "git.oblat.lv/alex/triggerssmith/internal/app" "git.oblat.lv/alex/triggerssmith/internal/config" "github.com/spf13/cobra" ) var opts = struct { ConfigPath *string Debug *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)), ) }) } var serveCmd = &cobra.Command{ Use: "serve", Short: "Start the server", Run: func(cmd *cobra.Command, args []string) { // defer func() { // if r := recover(); r != nil { // slog.Error("Application panicked", slog.Any("error", r)) // } // }() // configure logger if *opts.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}))) } slog.Debug("Starting server") // load config slog.Debug("Reading configuration", slog.String("path", *opts.ConfigPath)) cfg, err := config.LoadConfig(*opts.ConfigPath) if err != nil { slog.Error("Failed to load configuration", slog.String("path", *opts.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) /* // setup handlers 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) */ // start server /*addr := fmt.Sprintf("%s:%d", cfg.Server.Addr, cfg.Server.Port) slog.Debug("Binding listener", slog.String("address", addr)) ln, err := net.Listen("tcp", addr) if err != nil { slog.Error("Failed to start listener", slog.String("address", addr), slog.String("error", err.Error())) return } srv := &http.Server{ Addr: addr, Handler: handler, } slog.Info("Server started", slog.String("address", addr)) if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed { slog.Error("HTTP server stopped with error", slog.String("error", err.Error())) }*/ server := 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) server.SetHandler(handler) server.Init() var addr = net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port)) slog.Debug("Binding listener", slog.String("address", addr)) err = server.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)))) } for true { fmt.Scanln() if err := config.ReloadConfig(cfg); err != nil { slog.Error("Failed to reload configuration", slog.String("error", err.Error())) } else { slog.Info("Configuration reloaded", slog.Any("config", cfg)) var addr = net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port)) err = server.Reload(addr) if err != nil { slog.Error("Failed to restart server with new configuration", slog.String("error", err.Error())) } else { slog.Info("Server restarted with new configuration", slog.String("address", net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port)))) } } } }, } func init() { opts.Debug = serveCmd.Flags().BoolP("debug", "d", false, "Enable debug logs") opts.ConfigPath = serveCmd.Flags().StringP("config", "c", "config.yaml", "Path to configuration file") rootCmd.AddCommand(serveCmd) }