From 7d423e2182493a0189b6fe987913d11778128e90 Mon Sep 17 00:00:00 2001 From: OKAN TIKTAS Date: Mon, 26 Oct 2026 06:05:56 -0400 Subject: [PATCH] fix(web): return startup errors instead of panicking (#1620) Replace server startup panics with returned errors, print actionable CLI messages with exit status 1, and clarify the API token flags. Add regressions for missing web authentication credentials. Fixes GopeedLab/gopeed#1614 --- cmd/api/main.go | 8 +++++++- cmd/server.go | 12 +++++++----- cmd/server_test.go | 35 +++++++++++++++++++++++++++++++++++ cmd/web/flags.go | 4 ++-- cmd/web/main.go | 5 ++++- 5 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 cmd/server_test.go diff --git a/cmd/api/main.go b/cmd/api/main.go index c9ad6759..af304d5e 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -1,6 +1,9 @@ package main import ( + "fmt" + "os" + "github.com/GopeedLab/gopeed/cmd" "github.com/GopeedLab/gopeed/pkg/rest/model" ) @@ -13,5 +16,8 @@ func main() { Storage: model.StorageBolt, WebEnable: true, } - cmd.Start(cfg) + if err := cmd.Start(cfg); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } diff --git a/cmd/server.go b/cmd/server.go index 7a0af23f..b0ff3e6f 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -16,15 +16,16 @@ import ( //go:embed banner.txt var banner string -func Start(cfg *model.StartConfig) { - fmt.Println(banner) +func Start(cfg *model.StartConfig) error { srv, listener, err := rest.BuildServer(cfg) if err != nil { - panic(err) + return err } + fmt.Println(banner) downloadCfg, err := rest.Downloader.GetConfig() if err != nil { - panic(err) + listener.Close() + return err } if rest.Downloader.IsFirstLoad() { // Set default download config @@ -62,8 +63,9 @@ func Start(cfg *model.StartConfig) { fmt.Printf("Server start success on http://%s\n", listener.Addr().String()) if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed { - panic(err) + return err } + return nil } func watchExit() { diff --git a/cmd/server_test.go b/cmd/server_test.go new file mode 100644 index 00000000..fdda960b --- /dev/null +++ b/cmd/server_test.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "strings" + "testing" + + "github.com/GopeedLab/gopeed/pkg/rest/model" +) + +func TestStartInvalidWebAPITokenReturnsError(t *testing.T) { + tests := []struct { + name string + webAuth *model.WebAuth + }{ + {name: "no credentials"}, + {name: "missing username", webAuth: &model.WebAuth{Password: "secret"}}, + {name: "missing password", webAuth: &model.WebAuth{Username: "gopeed"}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := Start(&model.StartConfig{ + WebEnable: true, + ApiToken: "test-token", + WebAuth: tt.webAuth, + }) + if err == nil { + t.Fatal("Start() error = nil, want web authentication validation error") + } + if !strings.Contains(err.Error(), "web authentication username and password are required") { + t.Fatalf("Start() error = %q, want missing web authentication credentials", err) + } + }) + } +} diff --git a/cmd/web/flags.go b/cmd/web/flags.go index 98d4a8aa..cffd7ff0 100644 --- a/cmd/web/flags.go +++ b/cmd/web/flags.go @@ -52,8 +52,8 @@ func loadCliArgs() *args { flag.StringVar(cfg.Username, "username", "gopeed", "Web Authentication Username") cfg.Password = flag.String("p", "", "Web Authentication Password, if no password is set, web authentication will not be enabled") flag.StringVar(cfg.Password, "password", "", "Web Authentication Password, if no password is set, web authentication will not be enabled") - cfg.ApiToken = flag.String("T", "", "API token, it must be configured when using HTTP API in the case of enabling web authentication") - flag.StringVar(cfg.ApiToken, "api-token", "", "API token, it must be configured when using HTTP API in the case of enabling web authentication") + cfg.ApiToken = flag.String("T", "", "API token (requires web authentication with username and password)") + flag.StringVar(cfg.ApiToken, "api-token", "", "API token (requires web authentication with username and password)") flag.BoolVar(&cfg.MCPEnable, "mcp-enable", false, "Enable MCP endpoint") cfg.TempDir = flag.String("temp-dir", "", "Temporary directory (defaults to the OS temporary directory)") cfg.StorageDir = flag.String("d", "", "Storage directory") diff --git a/cmd/web/main.go b/cmd/web/main.go index 2b604d01..28aef482 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -62,7 +62,10 @@ func main() { WebFS: sub, WebAuth: webAuth, } - cmd.Start(cfg) + if err := cmd.Start(cfg); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } func isNotBlank(str *string) bool {