From c040d7c90d44b6436957ee2464c5be588780746e Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 4 Mar 2026 19:44:21 +0800 Subject: [PATCH] =?UTF-8?q?SDK=E4=B8=8B=E8=BD=BD=E4=B9=B1=E7=A0=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/default/httpdns/apps/sdk_doc.go | 3 +- .../default/httpdns/apps/sdk_download.go | 3 +- EdgeHttpDNS/.gitignore | 1 - EdgeHttpDNS/internal/configs/api_config.go | 171 ++++++++++++++++++ .../actions/default/httpdns/apps/sdk_doc.go | 3 +- .../default/httpdns/apps/sdk_download.go | 3 +- 6 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 EdgeHttpDNS/internal/configs/api_config.go diff --git a/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_doc.go b/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_doc.go index d02cd0c..31b8ab1 100644 --- a/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_doc.go +++ b/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_doc.go @@ -1,6 +1,7 @@ package apps import ( + "net/url" "os" "path/filepath" @@ -49,6 +50,6 @@ func (this *SdkDocAction) RunGet(params struct { } this.AddHeader("Content-Type", "text/markdown; charset=utf-8") - this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";") + this.AddHeader("Content-Disposition", "attachment; filename=\"sdk-doc.md\"; filename*=UTF-8''"+url.PathEscape(downloadName)) _, _ = this.ResponseWriter.Write(data) } diff --git a/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_download.go b/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_download.go index 09e41d2..b6d19e1 100644 --- a/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_download.go +++ b/EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk_download.go @@ -2,6 +2,7 @@ package apps import ( "io" + "net/url" "os" "path/filepath" @@ -58,7 +59,7 @@ func (this *SdkDownloadAction) RunGet(params struct { this.AddHeader("X-SDK-Filename", downloadName) } else { this.AddHeader("Content-Type", "application/zip") - this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";") + this.AddHeader("Content-Disposition", "attachment; filename=\"sdk-download\"; filename*=UTF-8''"+url.PathEscape(downloadName)) } this.AddHeader("X-Accel-Buffering", "no") _, _ = io.Copy(this.ResponseWriter, fp) diff --git a/EdgeHttpDNS/.gitignore b/EdgeHttpDNS/.gitignore index 96a2d00..419bf8d 100644 --- a/EdgeHttpDNS/.gitignore +++ b/EdgeHttpDNS/.gitignore @@ -1,5 +1,4 @@ *.zip edge-dns -configs/ logs/ data/ diff --git a/EdgeHttpDNS/internal/configs/api_config.go b/EdgeHttpDNS/internal/configs/api_config.go new file mode 100644 index 0000000..05e0739 --- /dev/null +++ b/EdgeHttpDNS/internal/configs/api_config.go @@ -0,0 +1,171 @@ +package configs + +import ( + "errors" + "os" + "path/filepath" + + "github.com/iwind/TeaGo/Tea" + "gopkg.in/yaml.v3" +) + +const ConfigFileName = "api_httpdns.yaml" +const oldConfigFileName = "api.yaml" + +var sharedAPIConfig *APIConfig + +type APIConfig struct { + OldRPC struct { + Endpoints []string `yaml:"endpoints"` + DisableUpdate bool `yaml:"disableUpdate"` + } `yaml:"rpc,omitempty"` + + RPCEndpoints []string `yaml:"rpc.endpoints,flow" json:"rpc.endpoints"` + RPCDisableUpdate bool `yaml:"rpc.disableUpdate" json:"rpc.disableUpdate"` + + NodeId string `yaml:"nodeId"` + Secret string `yaml:"secret"` + + ListenAddr string `yaml:"listenAddr"` + + HTTPSListenAddr string `yaml:"https.listenAddr" json:"https.listenAddr"` + HTTPSCert string `yaml:"https.cert" json:"https.cert"` + HTTPSKey string `yaml:"https.key" json:"https.key"` + + LogDir string `yaml:"logDir" json:"logDir"` +} + +func SharedAPIConfig() (*APIConfig, error) { + if sharedAPIConfig != nil { + return sharedAPIConfig, nil + } + + config, err := LoadAPIConfig() + if err != nil { + return nil, err + } + sharedAPIConfig = config + return config, nil +} + +func LoadAPIConfig() (*APIConfig, error) { + for _, filename := range []string{ConfigFileName, oldConfigFileName} { + var loadedData []byte + var loadedPath string + for _, candidate := range findConfigCandidates(filename) { + data, err := os.ReadFile(candidate) + if err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + loadedData = data + loadedPath = candidate + break + } + + if len(loadedData) == 0 { + if filename == oldConfigFileName { + continue + } + return nil, errors.New("no config file '" + ConfigFileName + "' found") + } + + config := &APIConfig{} + err := yaml.Unmarshal(loadedData, config) + if err != nil { + return nil, err + } + + err = config.Init() + if err != nil { + return nil, err + } + + if filename == oldConfigFileName { + config.OldRPC.Endpoints = nil + // 优先写回当前旧配置所在目录,避免依赖启动时工作目录 + _ = config.WriteFile(filepath.Join(filepath.Dir(loadedPath), ConfigFileName)) + } + + return config, nil + } + return nil, errors.New("no config file '" + ConfigFileName + "' found") +} + +func (c *APIConfig) Init() error { + if len(c.RPCEndpoints) == 0 && len(c.OldRPC.Endpoints) > 0 { + c.RPCEndpoints = c.OldRPC.Endpoints + c.RPCDisableUpdate = c.OldRPC.DisableUpdate + } + + if len(c.RPCEndpoints) == 0 { + return errors.New("no valid 'rpc.endpoints'") + } + + if len(c.NodeId) == 0 { + return errors.New("'nodeId' required") + } + if len(c.Secret) == 0 { + return errors.New("'secret' required") + } + + // 兼容旧配置中的 listenAddr + if len(c.HTTPSListenAddr) == 0 { + if len(c.ListenAddr) > 0 { + c.HTTPSListenAddr = c.ListenAddr + } else { + c.HTTPSListenAddr = ":8443" + } + } + + if len(c.HTTPSCert) == 0 { + // return errors.New("'https.cert' required") + } + if len(c.HTTPSKey) == 0 { + // return errors.New("'https.key' required") + } + return nil +} + +func (c *APIConfig) WriteFile(path string) error { + data, err := yaml.Marshal(c) + if err != nil { + return err + } + return os.WriteFile(path, data, 0666) +} + +func findConfigCandidates(filename string) []string { + candidates := []string{ + Tea.ConfigFile(filename), + Tea.ConfigFile(filepath.Join("configs", filename)), + filename, + filepath.Join("configs", filename), + } + + if exePath, err := os.Executable(); err == nil { + exeDir := filepath.Dir(exePath) + candidates = append(candidates, + filepath.Join(exeDir, filename), + filepath.Join(exeDir, "configs", filename), + filepath.Join(exeDir, "..", "configs", filename), + ) + } + + uniq := map[string]struct{}{} + result := make([]string, 0, len(candidates)) + for _, candidate := range candidates { + candidate = filepath.Clean(candidate) + if len(candidate) == 0 { + continue + } + if _, ok := uniq[candidate]; ok { + continue + } + uniq[candidate] = struct{}{} + result = append(result, candidate) + } + return result +} diff --git a/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_doc.go b/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_doc.go index 8286a46..f699fab 100644 --- a/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_doc.go +++ b/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_doc.go @@ -1,6 +1,7 @@ package apps import ( + "net/url" "os" "path/filepath" @@ -49,6 +50,6 @@ func (this *SdkDocAction) RunGet(params struct { } this.AddHeader("Content-Type", "text/markdown; charset=utf-8") - this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";") + this.AddHeader("Content-Disposition", "attachment; filename=\"sdk-doc.md\"; filename*=UTF-8''"+url.PathEscape(downloadName)) _, _ = this.ResponseWriter.Write(data) } diff --git a/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_download.go b/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_download.go index f184927..0423133 100644 --- a/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_download.go +++ b/EdgeUser/internal/web/actions/default/httpdns/apps/sdk_download.go @@ -2,6 +2,7 @@ package apps import ( "io" + "net/url" "os" "path/filepath" @@ -58,7 +59,7 @@ func (this *SdkDownloadAction) RunGet(params struct { this.AddHeader("X-SDK-Filename", downloadName) } else { this.AddHeader("Content-Type", "application/zip") - this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";") + this.AddHeader("Content-Disposition", "attachment; filename=\"sdk-download\"; filename*=UTF-8''"+url.PathEscape(downloadName)) } this.AddHeader("X-Accel-Buffering", "no") _, _ = io.Copy(this.ResponseWriter, fp)