mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-08 01:20:27 +00:00
feat: add MCP support (#1471)
This commit is contained in:
83 files changed
+3119
-363
No files matched your search
+24
-9
@@ -18,6 +18,7 @@ type args struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
ApiToken *string `json:"apiToken"`
|
||||
MCPEnable bool `json:"mcpEnable"`
|
||||
StorageDir *string `json:"storageDir"`
|
||||
WhiteDownloadDirs []string `json:"whiteDownloadDirs"`
|
||||
// DownloadConfig when the first time to start the server, it will be configured as initial value
|
||||
@@ -43,13 +44,21 @@ func parse() *args {
|
||||
func loadCliArgs() *args {
|
||||
cfg := &args{}
|
||||
cfg.Address = flag.String("A", "0.0.0.0", "Bind Address")
|
||||
flag.StringVar(cfg.Address, "address", "0.0.0.0", "Bind Address")
|
||||
cfg.Port = flag.Int("P", 9999, "Bind Port")
|
||||
flag.IntVar(cfg.Port, "port", 9999, "Bind Port")
|
||||
cfg.Username = flag.String("u", "gopeed", "Web Authentication Username")
|
||||
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")
|
||||
flag.BoolVar(&cfg.MCPEnable, "mcp-enable", false, "Enable MCP endpoint")
|
||||
cfg.StorageDir = flag.String("d", "", "Storage directory")
|
||||
whiteDownloadDirs := flag.String("w", "", "White download directories, comma-separated")
|
||||
flag.StringVar(cfg.StorageDir, "storage-dir", "", "Storage directory")
|
||||
whiteDownloadDirs := flag.String("white-download-dirs", "", "White download directories, comma-separated")
|
||||
cfg.configPath = flag.String("c", "./config.json", "Config file path")
|
||||
flag.StringVar(cfg.configPath, "config", "./config.json", "Config file path")
|
||||
flag.Parse()
|
||||
|
||||
// Parse white download directories from comma-separated string
|
||||
@@ -68,21 +77,23 @@ func loadCliArgs() *args {
|
||||
func overrideWithCliArgs(cfg *args, cliConfig *args) {
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
switch f.Name {
|
||||
case "A":
|
||||
case "A", "address":
|
||||
cfg.Address = cliConfig.Address
|
||||
case "P":
|
||||
case "P", "port":
|
||||
cfg.Port = cliConfig.Port
|
||||
case "u":
|
||||
case "u", "username":
|
||||
cfg.Username = cliConfig.Username
|
||||
case "p":
|
||||
case "p", "password":
|
||||
cfg.Password = cliConfig.Password
|
||||
case "T":
|
||||
case "T", "api-token":
|
||||
cfg.ApiToken = cliConfig.ApiToken
|
||||
case "d":
|
||||
case "mcp-enable":
|
||||
cfg.MCPEnable = cliConfig.MCPEnable
|
||||
case "d", "storage-dir":
|
||||
cfg.StorageDir = cliConfig.StorageDir
|
||||
case "w":
|
||||
case "white-download-dirs":
|
||||
cfg.WhiteDownloadDirs = cliConfig.WhiteDownloadDirs
|
||||
case "c":
|
||||
case "c", "config":
|
||||
cfg.configPath = cliConfig.configPath
|
||||
}
|
||||
})
|
||||
@@ -194,6 +205,10 @@ func loadEnvVars(cfg *args) {
|
||||
}
|
||||
field.Set(reflect.ValueOf(dirs))
|
||||
}
|
||||
} else if field.Kind() == reflect.Bool {
|
||||
if boolVal, err := strconv.ParseBool(envValue); err == nil {
|
||||
field.SetBool(boolVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
-18
@@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -10,6 +12,50 @@ import (
|
||||
"github.com/GopeedLab/gopeed/pkg/base"
|
||||
)
|
||||
|
||||
func TestLoadCliArgsLongNames(t *testing.T) {
|
||||
originalCommandLine := flag.CommandLine
|
||||
originalArgs := os.Args
|
||||
t.Cleanup(func() {
|
||||
flag.CommandLine = originalCommandLine
|
||||
os.Args = originalArgs
|
||||
})
|
||||
|
||||
flags := flag.NewFlagSet("gopeed-web-test", flag.ContinueOnError)
|
||||
flags.SetOutput(io.Discard)
|
||||
flag.CommandLine = flags
|
||||
os.Args = []string{
|
||||
"gopeed-web",
|
||||
"--address", "127.0.0.2",
|
||||
"--port", "8080",
|
||||
"--username", "admin",
|
||||
"--password", "password",
|
||||
"--api-token", "token",
|
||||
"--mcp-enable",
|
||||
"--storage-dir", "/tmp/gopeed",
|
||||
"--white-download-dirs", "/tmp/one,/tmp/two",
|
||||
"--config", "/tmp/gopeed.json",
|
||||
}
|
||||
|
||||
cfg := loadCliArgs()
|
||||
if *cfg.Address != "127.0.0.2" || *cfg.Port != 8080 || *cfg.Username != "admin" ||
|
||||
*cfg.Password != "password" || *cfg.ApiToken != "token" || !cfg.MCPEnable ||
|
||||
*cfg.StorageDir != "/tmp/gopeed" || !reflect.DeepEqual(cfg.WhiteDownloadDirs, []string{"/tmp/one", "/tmp/two"}) ||
|
||||
*cfg.configPath != "/tmp/gopeed.json" {
|
||||
t.Fatalf("unexpected long-name CLI config: %+v", cfg)
|
||||
}
|
||||
|
||||
for _, name := range []string{"A", "P", "u", "p", "T", "d", "c"} {
|
||||
if flags.Lookup(name) == nil {
|
||||
t.Errorf("legacy short flag %q is missing", name)
|
||||
}
|
||||
}
|
||||
for _, name := range []string{"M", "w"} {
|
||||
if flags.Lookup(name) != nil {
|
||||
t.Errorf("new option unexpectedly has short flag %q", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
// Create mock CLI configuration with command line default values
|
||||
cliDefaults := &args{
|
||||
@@ -165,14 +211,15 @@ func TestOverrideWithCliArgs(t *testing.T) {
|
||||
t.Run("flag override documentation", func(t *testing.T) {
|
||||
// Document the expected behavior for each flag:
|
||||
flagBehaviors := map[string]string{
|
||||
"A": "Should override cfg.Address with cliConfig.Address",
|
||||
"P": "Should override cfg.Port with cliConfig.Port",
|
||||
"u": "Should override cfg.Username with cliConfig.Username",
|
||||
"p": "Should override cfg.Password with cliConfig.Password",
|
||||
"T": "Should override cfg.ApiToken with cliConfig.ApiToken",
|
||||
"d": "Should override cfg.StorageDir with cliConfig.StorageDir",
|
||||
"w": "Should override cfg.WhiteDownloadDirs with cliConfig.WhiteDownloadDirs",
|
||||
"c": "Should override cfg.configPath with cliConfig.configPath",
|
||||
"A/address": "Should override cfg.Address with cliConfig.Address",
|
||||
"P/port": "Should override cfg.Port with cliConfig.Port",
|
||||
"u/username": "Should override cfg.Username with cliConfig.Username",
|
||||
"p/password": "Should override cfg.Password with cliConfig.Password",
|
||||
"T/api-token": "Should override cfg.ApiToken with cliConfig.ApiToken",
|
||||
"mcp-enable": "Should override cfg.MCPEnable with cliConfig.MCPEnable",
|
||||
"d/storage-dir": "Should override cfg.StorageDir with cliConfig.StorageDir",
|
||||
"white-download-dirs": "Should override cfg.WhiteDownloadDirs with cliConfig.WhiteDownloadDirs",
|
||||
"c/config": "Should override cfg.configPath with cliConfig.configPath",
|
||||
}
|
||||
|
||||
t.Log("Expected flag override behaviors:")
|
||||
@@ -187,21 +234,23 @@ func TestOverrideWithCliArgs(t *testing.T) {
|
||||
mockFlagVisit := func(config *args, cliConfig *args, flagName string) {
|
||||
// Simulate the switch statement in overrideWithCliArgs
|
||||
switch flagName {
|
||||
case "A":
|
||||
case "A", "address":
|
||||
config.Address = cliConfig.Address
|
||||
case "P":
|
||||
case "P", "port":
|
||||
config.Port = cliConfig.Port
|
||||
case "u":
|
||||
case "u", "username":
|
||||
config.Username = cliConfig.Username
|
||||
case "p":
|
||||
case "p", "password":
|
||||
config.Password = cliConfig.Password
|
||||
case "T":
|
||||
case "T", "api-token":
|
||||
config.ApiToken = cliConfig.ApiToken
|
||||
case "d":
|
||||
case "mcp-enable":
|
||||
config.MCPEnable = cliConfig.MCPEnable
|
||||
case "d", "storage-dir":
|
||||
config.StorageDir = cliConfig.StorageDir
|
||||
case "w":
|
||||
case "white-download-dirs":
|
||||
config.WhiteDownloadDirs = cliConfig.WhiteDownloadDirs
|
||||
case "c":
|
||||
case "c", "config":
|
||||
config.configPath = cliConfig.configPath
|
||||
default:
|
||||
t.Errorf("Unknown flag: %s", flagName)
|
||||
@@ -285,6 +334,20 @@ func TestOverrideWithCliArgs(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
flagName: "mcp-enable",
|
||||
setupConfig: func() *args {
|
||||
return &args{MCPEnable: false}
|
||||
},
|
||||
setupCliConfig: func() *args {
|
||||
return &args{MCPEnable: true}
|
||||
},
|
||||
verify: func(t *testing.T, cfg *args) {
|
||||
if !cfg.MCPEnable {
|
||||
t.Error("MCPEnable flag override failed: got false, want true")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
flagName: "d",
|
||||
setupConfig: func() *args {
|
||||
@@ -300,7 +363,7 @@ func TestOverrideWithCliArgs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
flagName: "w",
|
||||
flagName: "white-download-dirs",
|
||||
setupConfig: func() *args {
|
||||
return &args{WhiteDownloadDirs: []string{"/original/dir1", "/original/dir2"}}
|
||||
},
|
||||
@@ -402,6 +465,7 @@ func TestLoadConfigFile(t *testing.T) {
|
||||
"username": "testuser",
|
||||
"password": "testpass",
|
||||
"apiToken": "testtoken",
|
||||
"mcpEnable": true,
|
||||
"storageDir": "/test/storage",
|
||||
"downloadConfig": {
|
||||
"downloadDir": "/test/downloads",
|
||||
@@ -415,6 +479,7 @@ func TestLoadConfigFile(t *testing.T) {
|
||||
Username: stringPtr("testuser"),
|
||||
Password: stringPtr("testpass"),
|
||||
ApiToken: stringPtr("testtoken"),
|
||||
MCPEnable: true,
|
||||
StorageDir: stringPtr("/test/storage"),
|
||||
DownloadConfig: &base.DownloaderStoreConfig{
|
||||
DownloadDir: "/test/downloads",
|
||||
@@ -476,7 +541,7 @@ func TestLoadEnvVars(t *testing.T) {
|
||||
originalEnv := make(map[string]string)
|
||||
envKeys := []string{
|
||||
"GOPEED_ADDRESS", "GOPEED_PORT", "GOPEED_USERNAME",
|
||||
"GOPEED_PASSWORD", "GOPEED_APITOKEN", "GOPEED_STORAGEDIR",
|
||||
"GOPEED_PASSWORD", "GOPEED_APITOKEN", "GOPEED_MCPENABLE", "GOPEED_STORAGEDIR",
|
||||
"GOPEED_DOWNLOADCONFIG", "GOPEED_WHITEDOWNLOADDIRS",
|
||||
}
|
||||
for _, key := range envKeys {
|
||||
@@ -508,6 +573,7 @@ func TestLoadEnvVars(t *testing.T) {
|
||||
"GOPEED_USERNAME": "envuser",
|
||||
"GOPEED_PASSWORD": "envpass",
|
||||
"GOPEED_APITOKEN": "envtoken",
|
||||
"GOPEED_MCPENABLE": "true",
|
||||
"GOPEED_STORAGEDIR": "/env/storage",
|
||||
},
|
||||
expected: &args{
|
||||
@@ -516,6 +582,7 @@ func TestLoadEnvVars(t *testing.T) {
|
||||
Username: stringPtr("envuser"),
|
||||
Password: stringPtr("envpass"),
|
||||
ApiToken: stringPtr("envtoken"),
|
||||
MCPEnable: true,
|
||||
StorageDir: stringPtr("/env/storage"),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -50,6 +50,7 @@ func main() {
|
||||
StorageDir: storageDir,
|
||||
WhiteDownloadDirs: args.WhiteDownloadDirs,
|
||||
ApiToken: *args.ApiToken,
|
||||
MCPEnable: args.MCPEnable,
|
||||
DownloadConfig: args.DownloadConfig,
|
||||
ProductionMode: true,
|
||||
WebEnable: true,
|
||||
|
||||
Reference in new issue
Block a user