diff --git a/Dockerfile b/Dockerfile index adcc0af..bf5f574 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,4 +28,4 @@ RUN \ EXPOSE 8080 # Run -CMD ["/app/packagelock"] +CMD ["/app/packagelock start"] diff --git a/config/conf-init.go b/config/conf-init.go index fc48228..3db2a00 100644 --- a/config/conf-init.go +++ b/config/conf-init.go @@ -24,6 +24,7 @@ type ConfigProvider interface { GetString(string string) string SetDefault(key string, value any) Get(key string) any + GetBool(key string) bool } // TODO: How to test? diff --git a/go.mod b/go.mod index 22baf34..d9732b7 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -33,6 +34,7 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect diff --git a/go.sum b/go.sum index 9356de8..04c16e7 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -38,6 +39,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -78,6 +81,7 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= @@ -91,6 +95,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= diff --git a/main.go b/main.go index 7907bfe..930619a 100644 --- a/main.go +++ b/main.go @@ -12,18 +12,42 @@ import ( "time" "github.com/fsnotify/fsnotify" + "github.com/spf13/cobra" "github.com/spf13/viper" ) -// Linker Injections -// Version injection with Docker Build & ldflags -// Do not modify, init or change in code! -var AppVersion string +var ( + restartChan = make(chan struct{}) + quitChan = make(chan os.Signal, 1) + AppVersion string // Version injected with ldflags +) -// TODO: support for multiple network adapters. +// Root command using Cobra +var rootCmd = &cobra.Command{ + Use: "packagelock", + Short: "Packagelock CLI tool", + Long: `Packagelock CLI manages the server and other operations.`, +} -func main() { - // Start Viper for config management +// Start command to run the server +var startCmd = &cobra.Command{ + Use: "start", + Short: "Start the server", + Run: func(cmd *cobra.Command, args []string) { + startServer() + }, +} + +func init() { + // Add commands to rootCmd + rootCmd.AddCommand(startCmd) + + // Initialize Viper config + cobra.OnInitialize(initConfig) +} + +// initConfig initializes Viper and configures the application +func initConfig() { config.Config = config.StartViper(viper.New()) // If AppVersion is injected, set it in the configuration @@ -31,39 +55,42 @@ func main() { config.Config.SetDefault("general.app-version", AppVersion) } + // Check and create self-signed certificates if missing if _, err := os.Stat(config.Config.GetString("network.ssl-config.certificatepath")); os.IsNotExist(err) { fmt.Println("Certificate files missing, creating new self-signed.") - err := certs.CreateSelfSignedCert(config.Config.GetString("network.ssl-config.certificatepath"), config.Config.GetString("network.ssl-config.privatekeypath")) + err := certs.CreateSelfSignedCert( + config.Config.GetString("network.ssl-config.certificatepath"), + config.Config.GetString("network.ssl-config.privatekeypath")) if err != nil { fmt.Printf("Error creating self-signed certificate: %v\n", err) - return + os.Exit(1) } } +} +// startServer starts the Fiber server with appropriate configuration +func startServer() { fmt.Println(config.Config.AllSettings()) - // Channel to signal the restart - restartChan := make(chan struct{}) - quitChan := make(chan os.Signal, 1) signal.Notify(quitChan, os.Interrupt, syscall.SIGTERM) // Start the server in a goroutine go func() { for { - // Add Fiber routes router := server.AddRoutes(config.Config) - // Fiber does not use the standard http.Server // Setup server address from config serverAddr := config.Config.GetString("network.fqdn") + ":" + config.Config.GetString("network.port") - // Fiber specific server start + // Start server based on SSL config go func() { - fmt.Printf("Starting Fiber HTTPS server at https://%s...\n", serverAddr) - - // start ssl session if ssl:true is set in config file, else start http - if config.Config.Get("network.ssl") == true { - if err := server.ListenAndServeTLS(router.Router, config.Config.GetString("network.ssl-config.certificatepath"), config.Config.GetString("network.ssl-config.privatekeypath"), serverAddr); err != nil { + if config.Config.GetBool("network.ssl") { + fmt.Printf("Starting Fiber HTTPS server at https://%s...\n", serverAddr) + if err := server.ListenAndServeTLS( + router.Router, + config.Config.GetString("network.ssl-config.certificatepath"), + config.Config.GetString("network.ssl-config.privatekeypath"), + serverAddr); err != nil { fmt.Printf("Server error: %s\n", err) } } else { @@ -74,12 +101,10 @@ func main() { } }() - // Wait for either a restart signal or termination signal + // Handle restart or quit signals select { case <-restartChan: fmt.Println("Restarting Fiber server...") - - // Gracefully shutdown the Fiber server _, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := router.Router.Shutdown(); err != nil { @@ -90,8 +115,6 @@ func main() { case <-quitChan: fmt.Println("Shutting down Fiber server...") - - // Gracefully shutdown on quit signal _, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := router.Router.Shutdown(); err != nil { @@ -104,11 +127,11 @@ func main() { } }() - // Watch for configuration changes + // Watch for config changes config.Config.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) fmt.Println("Restarting to apply changes...") - restartChan <- struct{}{} // Send signal to restart the server + restartChan <- struct{}{} }) config.Config.WatchConfig() @@ -116,3 +139,11 @@ func main() { <-quitChan fmt.Println("Main process exiting.") } + +func main() { + // Execute the Cobra root command + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/templates/404.html b/templates/404.html index 8ff6f44..cc068c8 100644 --- a/templates/404.html +++ b/templates/404.html @@ -157,7 +157,7 @@